Refactor bram_writer and test script: improve code readability, update package installation method, and enhance image processing logic

This commit is contained in:
2025-04-17 01:24:18 +02:00
parent f363f09506
commit 9bf8c21957
2 changed files with 198 additions and 155 deletions

View File

@@ -1,150 +1,191 @@
library ieee; LIBRARY ieee;
use ieee.std_logic_1164.all; USE ieee.std_logic_1164.ALL;
use ieee.numeric_std.all; USE ieee.numeric_std.ALL;
entity bram_writer is ENTITY bram_writer IS
generic( GENERIC (
ADDR_WIDTH: POSITIVE :=16 ADDR_WIDTH : POSITIVE := 16;
IMG_SIZE : POSITIVE := 256 -- Dimensione immagine NxN
); );
port ( PORT (
clk : in std_logic; clk : IN STD_LOGIC;
aresetn : in std_logic; aresetn : IN STD_LOGIC;
s_axis_tdata : in std_logic_vector(7 downto 0); -- Interfaccia AXI Stream per ricezione dati
s_axis_tvalid : in std_logic; s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
s_axis_tready : out std_logic; s_axis_tvalid : IN STD_LOGIC;
s_axis_tlast : in std_logic; s_axis_tready : OUT STD_LOGIC;
s_axis_tlast : IN STD_LOGIC;
conv_addr: in std_logic_vector(ADDR_WIDTH-1 downto 0); -- Interfaccia di lettura BRAM per il modulo di convoluzione
conv_data: out std_logic_vector(6 downto 0); conv_addr : IN STD_LOGIC_VECTOR(ADDR_WIDTH - 1 DOWNTO 0);
conv_data : OUT STD_LOGIC_VECTOR(6 DOWNTO 0); -- solo 7 bit usati
start_conv: out std_logic; -- Controllo avvio/fine convoluzione
done_conv: in std_logic; start_conv : OUT STD_LOGIC;
done_conv : IN STD_LOGIC;
write_ok : out std_logic;
overflow : out std_logic;
underflow: out std_logic
-- LED di stato (come da immagine)
write_ok : OUT STD_LOGIC;
overflow : OUT STD_LOGIC;
underflow : OUT STD_LOGIC
); );
end entity bram_writer; END ENTITY bram_writer;
architecture rtl of bram_writer is ARCHITECTURE rtl OF bram_writer IS
component bram_controller is -- Componente BRAM (controller)
generic ( COMPONENT bram_controller IS
ADDR_WIDTH: POSITIVE :=16 GENERIC (
ADDR_WIDTH : POSITIVE := 16
); );
port ( PORT (
clk : in std_logic; clk : IN STD_LOGIC;
aresetn : in std_logic; aresetn : IN STD_LOGIC;
addr: in std_logic_vector(ADDR_WIDTH-1 downto 0); addr : IN STD_LOGIC_VECTOR(ADDR_WIDTH - 1 DOWNTO 0);
dout: out std_logic_vector(7 downto 0); dout : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
din: in std_logic_vector(7 downto 0); din : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
we: in std_logic we : IN STD_LOGIC
); );
end component; END COMPONENT;
CONSTANT total_px_expected : unsigned(ADDR_WIDTH - 1 DOWNTO 0) := to_unsigned(IMG_SIZE ** 2 - 1, ADDR_WIDTH); -- IMG_SIZE * IMG_SIZE
TYPE state_type IS (IDLE, RECEIVING, CHECK_START_CONV, CONVOLUTION);
SIGNAL state : state_type := IDLE;
-- Registri di stato e segnale interno -- Registri di stato e segnale interno
signal addr_cnt : unsigned(ADDR_WIDTH-1 downto 0) := (others => '0'); -- Contatore indirizzo BRAM signal addr_write : UNSIGNED(ADDR_WIDTH DOWNTO 0) := (OTHERS => '0'); -- Indirizzo BRAM
signal wr_enable : std_logic := '0'; -- Segnale di scrittura BRAM SIGNAL addr_bram : STD_LOGIC_VECTOR(ADDR_WIDTH - 1 DOWNTO 0) := (OTHERS => '0'); -- Indirizzo BRAM
signal din_reg : std_logic_vector(7 downto 0) := (others => '0'); -- Dato da scrivere in BRAM
signal fsm_state : integer range 0 to 3 := 0; -- Stato FSM
signal pixel_count: unsigned(ADDR_WIDTH-1 downto 0) := (others => '0'); -- Pixel ricevuti
signal total_px_expected : unsigned(ADDR_WIDTH-1 downto 0); -- IMG_SIZE * IMG_SIZE
signal dout_bram : std_logic_vector(7 downto 0); -- Dato letto dalla BRAM SIGNAL wr_enable : STD_LOGIC := '0'; -- Segnale di scrittura BRAM
SIGNAL din_reg : STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '0'); -- Dato da scrivere in BRAM
SIGNAL flag_of : STD_LOGIC := '0'; -- Flag overflow (se addr_count > total_px_expected)
begin SIGNAL dout_bram : STD_LOGIC_VECTOR(7 DOWNTO 0); -- Dato letto dalla BRAM
-- Calcolo totale pixel attesi = N x N SIGNAL s_axis_tready_int : STD_LOGIC;
total_px_expected <= to_unsigned(IMG_SIZE * IMG_SIZE, ADDR_WIDTH);
BEGIN
-- Instanziazione BRAM (scrive e legge) -- Instanziazione BRAM (scrive e legge)
BRAM_CTRL : bram_controller BRAM_CTRL : bram_controller
generic map ( GENERIC MAP(
ADDR_WIDTH => ADDR_WIDTH ADDR_WIDTH => ADDR_WIDTH
) )
port map ( PORT MAP(
clk => clk, clk => clk,
aresetn => aresetn, aresetn => aresetn,
addr => conv_addr, addr => addr_bram,
dout => dout_bram, dout => dout_bram,
din => din_reg, din => din_reg,
we => wr_enable we => wr_enable
); );
-- Usiamo solo i primi 7 bit del dato letto per conv_data -- AXIS
conv_data <= dout_bram(6 downto 0); s_axis_tready <= s_axis_tready_int;
-- Segnale di lettura BRAM per il modulo di convoluzione
conv_data <= dout_bram(6 DOWNTO 0);
-- FSM principale -- FSM principale
process(clk, aresetn) PROCESS (clk)
begin BEGIN
if aresetn = '0' then IF rising_edge(clk) THEN
-- Reset asincrono IF aresetn = '0' THEN
fsm_state <= 0; -- Reset sincrono
addr_cnt <= (others => '0'); state <= IDLE;
pixel_count <= (others => '0'); addr_bram <= (OTHERS => '0');
wr_enable <= '0'; addr_write <= (OTHERS => '0');
s_axis_tready <= '0'; wr_enable <= '0';
write_ok <= '0'; write_ok <= '0';
overflow <= '0'; overflow <= '0';
underflow <= '0'; underflow <= '0';
start_conv <= '0'; start_conv <= '0';
flag_of <= '0';
elsif rising_edge(clk) then s_axis_tready_int <= '0';
-- Valori di default ogni ciclo
wr_enable <= '0';
start_conv <= '0';
write_ok <= '0';
overflow <= '0';
underflow <= '0';
case fsm_state is ELSE
-- Valori di default ogni ciclo
wr_enable <= '0';
start_conv <= '0';
when 0 => -- Stato IDLE/RICEZIONE (legge dati da AXIS) write_ok <= '0';
s_axis_tready <= '1'; -- pronto a ricevere overflow <= '0';
underflow <= '0';
if s_axis_tvalid = '1' then addr_bram <= conv_addr; -- Passa indirizzo al modulo di convoluzione
-- Registra dato ricevuto e scrive in BRAM
din_reg <= s_axis_tdata;
wr_enable <= '1';
addr_cnt <= addr_cnt + 1;
pixel_count <= pixel_count + 1;
-- Se <20> l'ultimo pixel del pacchetto CASE state IS
if s_axis_tlast = '1' then
fsm_state <= 1;
end if;
end if;
when 1 => -- Controllo overflow/underflow WHEN IDLE =>
s_axis_tready <= '0'; addr_write <= (OTHERS => '0');
flag_of <= '0'; -- Reset flag overflow
s_axis_tready_int <= '1'; -- Pronto a ricevere dati
if pixel_count = total_px_expected then IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
write_ok <= '1'; -- LED OK -- Registra dato ricevuto e scrive in BRAM
elsif pixel_count < total_px_expected then din_reg <= s_axis_tdata;
underflow <= '1'; -- LED underflow addr_bram <= STD_LOGIC_VECTOR(addr_write(ADDR_WIDTH - 1 DOWNTO 0));
else wr_enable <= '1';
overflow <= '1'; -- LED overflow
end if;
fsm_state <= 2; -- Vai a start convoluzione s_axis_tready_int <= '1'; -- Pronto a ricevere nuovi dati
when 2 => -- Avvia convoluzione state <= RECEIVING;
start_conv <= '1'; -- Segnale per far partire modulo Conv END IF;
if done_conv = '1' then WHEN RECEIVING => -- Stato RICEZIONE (legge dati da AXIS)
-- Reset e torna a ricevere nuovi dati IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
fsm_state <= 0; -- Registra dato ricevuto e scrive in BRAM
addr_cnt <= (others => '0'); din_reg <= s_axis_tdata;
pixel_count <= (others => '0'); addr_bram <= STD_LOGIC_VECTOR(addr_write(ADDR_WIDTH - 1 DOWNTO 0));
end if; wr_enable <= '1';
addr_write <= addr_write + 1;
when others => s_axis_tready_int <= '1'; -- Pronto a ricevere nuovi dati
fsm_state <= 0;
end case;
end if;
end process;
end architecture; -- Se <20> l'ultimo pixel del pacchetto
IF s_axis_tlast = '1' THEN
state <= CHECK_START_CONV;
END IF;
END IF;
WHEN CHECK_START_CONV => -- Controllo overflow/underflow
s_axis_tready_int <= '0';
IF flag_of = '1' THEN
overflow <= '1'; -- LED overflow
state <= IDLE; -- Torna a stato IDLE
ELSIF addr_write < total_px_expected THEN
underflow <= '1'; -- LED underflow
state <= IDLE; -- Torna a stato IDLE
ELSIF addr_write = total_px_expected THEN
write_ok <= '1'; -- LED OK
start_conv <= '1'; -- Segnale per far partire modulo Conv
state <= CONVOLUTION; -- Vai a start convoluzione
END IF;
WHEN CONVOLUTION => -- Avvia convoluzione
s_axis_tready_int <= '0';
IF done_conv = '1' THEN
state <= IDLE;
s_axis_tready_int <= '1';
END IF;
END CASE;
-- il controllo viene eseguito qui nel caso in cui addr_write vada in overflow e risulti quindi minore di total_px_expected
IF addr_write > total_px_expected AND flag_of = '0' THEN
flag_of <= '1';
END IF;
END IF;
END IF;
END PROCESS;
END ARCHITECTURE;

View File

@@ -1,87 +1,89 @@
import sys
import subprocess
def install_and_import(package, package_name=None): def install_and_import(package, package_name=None):
if package_name is None: if package_name is None:
package_name = package package_name = package
import importlib import importlib
try: try:
importlib.import_module(package) importlib.import_module(package)
except ImportError: except ImportError:
import pip subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
pip.main(['install', package_name])
finally: finally:
globals()[package] = importlib.import_module(package) globals()[package] = importlib.import_module(package)
install_and_import("Serial", "pyserial") install_and_import("serial", "pyserial")
install_and_import("PIL", "pillow") install_and_import("PIL", "pillow")
install_and_import("tqdm") install_and_import("tqdm")
install_and_import("numpy") install_and_import("numpy")
install_and_import("scipy") install_and_import("scipy")
from serial import Serial from serial import Serial
import serial.tools.list_ports import serial.tools.list_ports
from tqdm import tqdm from tqdm import tqdm
from PIL import Image from PIL import Image
from scipy.signal import convolve2d from scipy.signal import convolve2d
import numpy as np import numpy as np
IMAGE_NAME2="test2.png" IMAGE_NAME2 = "test2.png"
IMAGE_NAME1="test1.png" IMAGE_NAME1 = "test1.png"
BASYS3_PID=0x6010 BASYS3_PID = 0x6010
BASYS3_VID=0x0403 BASYS3_VID = 0x0403
IMG_HEIGHT=256 IMG_HEIGHT = 256
IMG_WIDTH=256 IMG_WIDTH = 256
dev="" dev = ""
for port in serial.tools.list_ports.comports(): for port in serial.tools.list_ports.comports():
if(port.vid==BASYS3_VID and port.pid==BASYS3_PID): if (port.vid == BASYS3_VID and port.pid == BASYS3_PID):
dev=port.device dev = port.device
if not dev: if not dev:
raise RuntimeError("Basys 3 Not Found!") raise RuntimeError("Basys 3 Not Found!")
test_n=int(input("Insert test number (1 or 2): ").strip()) test_n = int(input("Insert test number (1 or 2): ").strip())
if(test_n not in [1,2]): if test_n not in [1, 2]:
raise RuntimeError("Test numer must be be 1 or 2") raise RuntimeError("Test number must be 1 or 2")
dev=Serial(dev,115200) dev = Serial(dev, 115200)
img=Image.open(IMAGE_NAME1 if test_n==1 else IMAGE_NAME2) img = Image.open(IMAGE_NAME1 if test_n == 1 else IMAGE_NAME2)
mat=np.asarray(img,dtype=np.uint8) if img.mode != "RGB":
img = img.convert("RGB")
mat = np.asarray(img, dtype=np.uint8)
mat=mat[:,:,:3] mat = mat[:, :, :3]
if(mat.max()>127): if mat.max() > 127:
mat=mat//2 mat = mat // 2
buff=mat.tobytes() buff = mat.tobytes()
mat=np.sum(mat,axis=2)//3 mat_gray = np.sum(mat, axis=2) // 3
sim_img=convolve2d(mat,[[-1,-1,-1],[-1,8,-1],[-1,-1,-1]], mode="same") sim_img = convolve2d(mat_gray, [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]], mode="same")
sim_img[sim_img<0]=0 sim_img[sim_img < 0] = 0
sim_img[sim_img>127]=127 sim_img[sim_img > 127] = 127
sim_img = sim_img.astype(np.uint8)
dev.write(b'\xff') dev.write(b'\xff')
for i in tqdm(range(IMG_HEIGHT)): for i in tqdm(range(IMG_HEIGHT)):
dev.write(buff[(i)*IMG_WIDTH*3:(i+1)*IMG_WIDTH*3]) dev.write(buff[i * IMG_WIDTH * 3:(i + 1) * IMG_WIDTH * 3])
dev.write(b'\xf1') dev.write(b'\xf1')
dev.flush() dev.flush()
res=dev.read(IMG_HEIGHT*IMG_WIDTH+2) res = dev.read(IMG_HEIGHT * IMG_WIDTH + 2)
res_img=np.frombuffer(res[1:-1],dtype=np.uint8) res_img = np.frombuffer(res[1:-1], dtype=np.uint8)
res_img = res_img.reshape((IMG_HEIGHT, IMG_WIDTH))
res_img=res_img.reshape((IMG_HEIGHT,IMG_WIDTH)) im = Image.fromarray(res_img)
assert np.all(res_img==sim_img), "Image Mismatch!"
im=Image.fromarray(np.uint8(res_img))
im.show() im.show()
if np.all(res_img != sim_img):
print("Image Mismatch!")
dev.close()