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');
addr_write <= (OTHERS => '0');
wr_enable <= '0'; wr_enable <= '0';
s_axis_tready <= '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';
ELSE
-- Valori di default ogni ciclo -- Valori di default ogni ciclo
wr_enable <= '0'; wr_enable <= '0';
start_conv <= '0'; start_conv <= '0';
write_ok <= '0'; write_ok <= '0';
overflow <= '0'; overflow <= '0';
underflow <= '0'; underflow <= '0';
case fsm_state is addr_bram <= conv_addr; -- Passa indirizzo al modulo di convoluzione
when 0 => -- Stato IDLE/RICEZIONE (legge dati da AXIS) CASE state IS
s_axis_tready <= '1'; -- pronto a ricevere
if s_axis_tvalid = '1' then WHEN IDLE =>
addr_write <= (OTHERS => '0');
flag_of <= '0'; -- Reset flag overflow
s_axis_tready_int <= '1'; -- Pronto a ricevere dati
IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
-- Registra dato ricevuto e scrive in BRAM -- Registra dato ricevuto e scrive in BRAM
din_reg <= s_axis_tdata; din_reg <= s_axis_tdata;
addr_bram <= STD_LOGIC_VECTOR(addr_write(ADDR_WIDTH - 1 DOWNTO 0));
wr_enable <= '1'; wr_enable <= '1';
addr_cnt <= addr_cnt + 1;
pixel_count <= pixel_count + 1; s_axis_tready_int <= '1'; -- Pronto a ricevere nuovi dati
state <= RECEIVING;
END IF;
WHEN RECEIVING => -- Stato RICEZIONE (legge dati da AXIS)
IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
-- Registra dato ricevuto e scrive in BRAM
din_reg <= s_axis_tdata;
addr_bram <= STD_LOGIC_VECTOR(addr_write(ADDR_WIDTH - 1 DOWNTO 0));
wr_enable <= '1';
addr_write <= addr_write + 1;
s_axis_tready_int <= '1'; -- Pronto a ricevere nuovi dati
-- Se <20> l'ultimo pixel del pacchetto -- Se <20> l'ultimo pixel del pacchetto
if s_axis_tlast = '1' then IF s_axis_tlast = '1' THEN
fsm_state <= 1; state <= CHECK_START_CONV;
end if; END IF;
end if; END IF;
when 1 => -- Controllo overflow/underflow WHEN CHECK_START_CONV => -- Controllo overflow/underflow
s_axis_tready <= '0'; s_axis_tready_int <= '0';
if pixel_count = total_px_expected then IF flag_of = '1' THEN
write_ok <= '1'; -- LED OK
elsif pixel_count < total_px_expected then
underflow <= '1'; -- LED underflow
else
overflow <= '1'; -- LED overflow overflow <= '1'; -- LED overflow
end if;
fsm_state <= 2; -- Vai a start convoluzione 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
when 2 => -- Avvia convoluzione
start_conv <= '1'; -- Segnale per far partire modulo Conv start_conv <= '1'; -- Segnale per far partire modulo Conv
state <= CONVOLUTION; -- Vai a start convoluzione
END IF;
if done_conv = '1' then WHEN CONVOLUTION => -- Avvia convoluzione
-- Reset e torna a ricevere nuovi dati s_axis_tready_int <= '0';
fsm_state <= 0;
addr_cnt <= (others => '0');
pixel_count <= (others => '0');
end if;
when others => IF done_conv = '1' THEN
fsm_state <= 0; state <= IDLE;
end case; s_axis_tready_int <= '1';
end if; END IF;
end process; END CASE;
end architecture; -- 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()