Refactor bram_writer and test script: improve code readability, update package installation method, and enhance image processing logic
This commit is contained in:
@@ -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;
|
||||||
|
IMG_SIZE : POSITIVE := 256 -- Dimensione immagine NxN
|
||||||
|
);
|
||||||
|
PORT (
|
||||||
|
clk : IN STD_LOGIC;
|
||||||
|
aresetn : IN STD_LOGIC;
|
||||||
|
|
||||||
|
-- Interfaccia AXI Stream per ricezione dati
|
||||||
|
s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
|
||||||
|
s_axis_tvalid : IN STD_LOGIC;
|
||||||
|
s_axis_tready : OUT STD_LOGIC;
|
||||||
|
s_axis_tlast : IN STD_LOGIC;
|
||||||
|
|
||||||
|
-- Interfaccia di lettura BRAM per il modulo di convoluzione
|
||||||
|
conv_addr : IN STD_LOGIC_VECTOR(ADDR_WIDTH - 1 DOWNTO 0);
|
||||||
|
conv_data : OUT STD_LOGIC_VECTOR(6 DOWNTO 0); -- solo 7 bit usati
|
||||||
|
|
||||||
|
-- Controllo avvio/fine convoluzione
|
||||||
|
start_conv : OUT STD_LOGIC;
|
||||||
|
done_conv : IN 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;
|
||||||
|
|
||||||
|
ARCHITECTURE rtl OF bram_writer IS
|
||||||
|
|
||||||
|
-- Componente BRAM (controller)
|
||||||
|
COMPONENT bram_controller IS
|
||||||
|
GENERIC (
|
||||||
ADDR_WIDTH : POSITIVE := 16
|
ADDR_WIDTH : POSITIVE := 16
|
||||||
);
|
);
|
||||||
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);
|
|
||||||
s_axis_tvalid : 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);
|
|
||||||
conv_data: out std_logic_vector(6 downto 0);
|
|
||||||
|
|
||||||
start_conv: out std_logic;
|
|
||||||
done_conv: in std_logic;
|
|
||||||
|
|
||||||
write_ok : out std_logic;
|
|
||||||
overflow : out std_logic;
|
|
||||||
underflow: out std_logic
|
|
||||||
|
|
||||||
|
addr : IN STD_LOGIC_VECTOR(ADDR_WIDTH - 1 DOWNTO 0);
|
||||||
|
dout : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
|
||||||
|
din : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
|
||||||
|
we : IN STD_LOGIC
|
||||||
);
|
);
|
||||||
end entity bram_writer;
|
END COMPONENT;
|
||||||
|
|
||||||
architecture rtl of bram_writer is
|
CONSTANT total_px_expected : unsigned(ADDR_WIDTH - 1 DOWNTO 0) := to_unsigned(IMG_SIZE ** 2 - 1, ADDR_WIDTH); -- IMG_SIZE * IMG_SIZE
|
||||||
|
|
||||||
component bram_controller is
|
TYPE state_type IS (IDLE, RECEIVING, CHECK_START_CONV, CONVOLUTION);
|
||||||
generic (
|
SIGNAL state : state_type := IDLE;
|
||||||
ADDR_WIDTH: POSITIVE :=16
|
|
||||||
);
|
|
||||||
port (
|
|
||||||
clk : in std_logic;
|
|
||||||
aresetn : in std_logic;
|
|
||||||
|
|
||||||
addr: in std_logic_vector(ADDR_WIDTH-1 downto 0);
|
|
||||||
dout: out std_logic_vector(7 downto 0);
|
|
||||||
din: in std_logic_vector(7 downto 0);
|
|
||||||
we: in std_logic
|
|
||||||
);
|
|
||||||
end component;
|
|
||||||
|
|
||||||
-- 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;
|
||||||
@@ -1,29 +1,27 @@
|
|||||||
|
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
|
||||||
|
|
||||||
@@ -46,30 +44,33 @@ if not dev:
|
|||||||
|
|
||||||
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)
|
||||||
|
if img.mode != "RGB":
|
||||||
|
img = img.convert("RGB")
|
||||||
mat = np.asarray(img, dtype=np.uint8)
|
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()
|
||||||
@@ -77,11 +78,12 @@ 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()
|
||||||
|
|||||||
Reference in New Issue
Block a user