Skip to content

Commit cb77964

Browse files
committed
Merge branch 'xneckat00-axis-vector2packet' into 'devel'
Neckar axis vector2packet See merge request ndk/ndk-fpga!411
2 parents c46000e + ab1427f commit cb77964

20 files changed

Lines changed: 1066 additions & 1 deletion

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
# Copyright (C) 2026 CESNET z. s. p. o.
3+
# Author(s): Tomáš Neckař <xneckat00@stud.fit.vut.cz>
4+
5+
# Packages
6+
lappend PACKAGES "$OFM_PATH/comp/base/pkg/math_pack.vhd"
7+
8+
# Components
9+
lappend COMPONENTS [ list "GEN_MUX" "$OFM_PATH/comp/base/logic/mux" "FULL" ]
10+
11+
# Files
12+
lappend MOD "$ENTITY_BASE/axis_vector2packet.vhd"
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
-- SPDX-License-Identifier: BSD-3-Clause
2+
-- Copyright (C) 2026 CESNET z. s. p. o.
3+
-- Author(s): Tomáš Neckař <xneckat00@stud.fit.vut.cz>
4+
5+
library IEEE;
6+
use IEEE.std_logic_1164.all;
7+
use IEEE.numeric_std.all;
8+
9+
use work.math_pack.all;
10+
11+
entity AXIS_VECTOR2PACKET is
12+
generic (
13+
-- width of RX Logic vector data signal in bits
14+
RX_TDATA_WIDTH : natural := 64;
15+
-- width of TX AXI-Stream data signal in bits - supported are only powers of 2
16+
TX_TDATA_WIDTH : natural := 64;
17+
-- width of TX AXI-Stream user signal in bits
18+
TUSER_WIDTH : natural := 64
19+
);
20+
port (
21+
-- =========================================================================
22+
-- Clock and reset signals
23+
-- =========================================================================
24+
CLK : in std_logic;
25+
RESET : in std_logic;
26+
27+
-- =========================================================================
28+
-- RX Logic vector interface (CLK)
29+
-- =========================================================================
30+
RX_TDATA : in std_logic_vector(RX_TDATA_WIDTH-1 downto 0);
31+
RX_TUSER : in std_logic_vector(TUSER_WIDTH-1 downto 0);
32+
RX_TVALID : in std_logic;
33+
RX_TREADY : out std_logic;
34+
35+
-- =========================================================================
36+
-- TX AXI-Stream interface (CLK)
37+
-- =========================================================================
38+
TX_TDATA : out std_logic_vector(TX_TDATA_WIDTH-1 downto 0);
39+
TX_TUSER : out std_logic_vector(TUSER_WIDTH-1 downto 0);
40+
TX_TKEEP : out std_logic_vector(TX_TDATA_WIDTH/8-1 downto 0);
41+
TX_TLAST : out std_logic;
42+
TX_TVALID : out std_logic;
43+
TX_TREADY : in std_logic
44+
);
45+
end entity;
46+
47+
architecture FULL of AXIS_VECTOR2PACKET is
48+
49+
constant PACKETS_PER_VECTOR : natural := div_roundup(RX_TDATA_WIDTH, TX_TDATA_WIDTH); -- (RX_TDATA_WIDTH + TX_TDATA_WIDTH - 1) / TX_TDATA_WIDTH;
50+
constant PADDED_WIDTH : natural := PACKETS_PER_VECTOR * TX_TDATA_WIDTH; -- PAD_BITS + RX_TDATA_WIDTH
51+
constant PAD_BITS : natural := PADDED_WIDTH - RX_TDATA_WIDTH; -- count of padding bits
52+
53+
signal rx_data_padded : std_logic_vector(PADDED_WIDTH-1 downto 0); -- PAD_BITS & RX_TDATA_WIDTH
54+
55+
signal full : std_logic; -- whether component has unsent data or not
56+
signal sel : unsigned(max(1,log2(PACKETS_PER_VECTOR))-1 downto 0) := (others => '0'); -- selects range to send from reg_data as a axis frame
57+
58+
signal reg_data : std_logic_vector(PADDED_WIDTH-1 downto 0);
59+
signal reg_user : std_logic_vector(TUSER_WIDTH-1 downto 0);
60+
61+
begin
62+
-- pad data so that the last frame isnt out of range
63+
padding_g: if PAD_BITS > 0 generate
64+
rx_data_padded <= std_logic_vector(to_unsigned(0, PAD_BITS)) & RX_TDATA;
65+
else generate
66+
rx_data_padded <= RX_TDATA;
67+
end generate;
68+
69+
RX_TREADY <= not full;
70+
TX_TVALID <= full;
71+
72+
-- the component is either ready to receive data or transmits valid data
73+
full_p : process (CLK)
74+
begin
75+
if (rising_edge(CLK)) then
76+
if (RESET = '1') then
77+
full <= '0';
78+
elsif (full = '0') then -- RX_TREADY = '1'
79+
if (RX_TVALID = '1') then
80+
-- if received valid data, set full (drives RX_TREADY low)
81+
full <= '1';
82+
end if;
83+
else -- TX_TVALID = '1'
84+
if (TX_TREADY = '1') then
85+
if (sel = PACKETS_PER_VECTOR - 1) then
86+
-- if sent last frame, clear full (drives RX_TREADY high)
87+
full <= '0';
88+
end if;
89+
end if;
90+
end if;
91+
end if;
92+
end process;
93+
94+
-- load new data to registers when RX side ready and valid
95+
regs_p : process (CLK)
96+
begin
97+
if (rising_edge(CLK)) then
98+
if (RESET = '0') then
99+
if (RX_TREADY = '1' and RX_TVALID = '1') then
100+
reg_data <= rx_data_padded;
101+
reg_user <= RX_TUSER;
102+
end if;
103+
end if;
104+
end if;
105+
end process;
106+
107+
-- if tx side ready, increments frame counter (sel) until last
108+
sel_p : process (CLK)
109+
begin
110+
if rising_edge(CLK) then
111+
if (RESET = '1') then
112+
sel <= (others => '0');
113+
elsif (full = '0') then -- RX_TREADY = '1'
114+
if (RX_TVALID = '1') then
115+
sel <= (others => '0');
116+
end if;
117+
else -- TX_TVALID = '1'
118+
if (TX_TREADY = '1') then
119+
if (sel < PACKETS_PER_VECTOR - 1) then
120+
sel <= sel + 1;
121+
end if;
122+
end if;
123+
end if;
124+
end if;
125+
end process;
126+
127+
-- select range to send from reg_data as a axis frame
128+
mux_i : entity work.GEN_MUX
129+
generic map (
130+
DATA_WIDTH => TX_TDATA_WIDTH,
131+
MUX_WIDTH => PACKETS_PER_VECTOR
132+
) port map (
133+
DATA_IN => reg_data,
134+
SEL => std_logic_vector(sel),
135+
DATA_OUT => TX_TDATA
136+
);
137+
138+
TX_TUSER <= reg_user;
139+
TX_TLAST <= '1' when (sel = PACKETS_PER_VECTOR - 1) else '0';
140+
141+
tx_tkeep_p : process (sel)
142+
begin
143+
TX_TKEEP <= (others => '1');
144+
-- if last frame, mark fully padded bytes (as keep = 0)
145+
if (sel = PACKETS_PER_VECTOR - 1 and PAD_BITS >= 8) then
146+
TX_TKEEP(TX_TDATA_WIDTH / 8 - 1 downto (TX_TDATA_WIDTH - PAD_BITS - 1) / 8 + 1) <= (others => '0');
147+
end if;
148+
end process;
149+
end architecture;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. _axis_vector2packet:
2+
3+
AXIS_VECTOR2PACKET
4+
------------
5+
It's recommended that TX_TDATA_WIDTH is a power of 2 and at least one byte.
6+
7+
.. vhdl:autoentity:: AXIS_VECTOR2PACKET
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
# Copyright (C) 2026 CESNET z. s. p. o.
3+
# Author(s): Tomáš Neckař <xneckat00@stud.fit.vut.cz>
4+
5+
TOP_LEVEL_ENT=AXIS_VECTOR2PACKET
6+
7+
SYNTH=quartus
8+
export DEVICE=AGILEX
9+
10+
CLK_PORTS=CLK
11+
CLK_PERIOD=5.0
12+
13+
.PHONY: all
14+
all: comp
15+
16+
include ../../../../../build/Makefile
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (C) 2026 CESNET z. s. p. o.
2+
# Author(s): Tomáš Neckař <xneckat00@stud.fit.vut.cz>
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
5+
lappend COMPONENTS [ list "SV_LV_AXI_BASE" "$OFM_PATH/comp/uvm/logic_vector_array_axi" "FULL"]
6+
lappend COMPONENTS [ list "SV_RESET_BASE" "$OFM_PATH/comp/uvm/reset" "FULL"]
7+
8+
lappend MOD "$ENTITY_BASE/tbench/env/pkg.sv"
9+
lappend MOD "$ENTITY_BASE/tbench/test/pkg.sv"
10+
lappend MOD "$ENTITY_BASE/tbench/generic.sv"
11+
lappend MOD "$ENTITY_BASE/tbench/testbench.sv"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (C) 2026 CESNET z. s. p. o.
2+
# Author(s): Tomáš Neckař <xneckat00@stud.fit.vut.cz>
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
5+
add wave -noupdate -group "RESET" "/testbench/reset/*"
6+
add wave -noupdate -group "RX" "/testbench/axi_rx/*"
7+
add wave -noupdate -group "TX" "/testbench/axi_tx/*"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (C) 2026 CESNET z. s. p. o.
2+
// Author(s): Tomáš Neckař <xneckat00@stud.fit.vut.cz>
3+
// SPDX-License-Identifier: BSD-3-Clause
4+
5+
class config_sequence extends uvm_object;
6+
`ndk_object_utils(uvm_vector2packet::config_sequence)
7+
8+
int unsigned space_size_min = 0;
9+
int unsigned space_size_max = 200;
10+
11+
int unsigned frame_size_min = 1;
12+
int unsigned frame_size_max = 1500;
13+
14+
int unsigned rdy_probability_min = 10;
15+
int unsigned rdy_probability_max = 100;
16+
17+
18+
function new(string name = "uvm_vector2packet::config_sequence");
19+
super.new(name);
20+
endfunction
21+
endclass
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright (C) 2026 CESNET z. s. p. o.
2+
// Author(s): Tomáš Neckař <xneckat00@stud.fit.vut.cz>
3+
// SPDX-License-Identifier: BSD-3-Clause
4+
5+
class env #(
6+
int unsigned RX_ITEMS,
7+
int unsigned RX_ITEM_WIDTH,
8+
int unsigned TX_ITEMS,
9+
int unsigned TX_ITEM_WIDTH,
10+
int unsigned TUSER_WIDTH
11+
) extends uvm_env;
12+
`uvm_component_param_utils(uvm_vector2packet::env #(RX_ITEMS, RX_ITEM_WIDTH, TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH));
13+
14+
// Virtual sequencer
15+
sequencer #(RX_ITEMS, RX_ITEM_WIDTH, TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH) m_sequencer;
16+
17+
// RESET interface
18+
protected uvm_reset::agent m_reset;
19+
// RX environments
20+
protected uvm_axi::agent_rx #(RX_ITEMS, RX_ITEM_WIDTH, TUSER_WIDTH) m_rx;
21+
// TX environments
22+
protected uvm_axi::agent_tx #(TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH) m_tx;
23+
24+
// Scoreboard
25+
protected scoreboard #(TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH) m_sc;
26+
// Model instance
27+
protected uvm_vector2packet::model #(RX_ITEMS, RX_ITEM_WIDTH, TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH) m_model;
28+
29+
// Constructor
30+
function new(string name, uvm_component parent = null);
31+
super.new(name, parent);
32+
endfunction
33+
34+
function int unsigned used();
35+
int unsigned ret = 0;
36+
ret |= (m_model.used() != 0);
37+
ret |= (m_sc.used() != 0);
38+
return ret;
39+
endfunction
40+
41+
// Create base components of environment.
42+
function void build_phase(uvm_phase phase);
43+
uvm_reset::config_item m_cfg_reset;
44+
uvm_axi::config_item m_cfg_rx;
45+
uvm_axi::config_item m_cfg_tx;
46+
47+
//Call parents function build_phase
48+
super.build_phase(phase);
49+
50+
//Create reset environment
51+
m_cfg_reset = new;
52+
m_cfg_reset.active = UVM_ACTIVE; // Activly driven environment
53+
// interface register name have to be same in testbench uvm_config_db#(...)::set();
54+
m_cfg_reset.interface_name = "vif_reset";
55+
uvm_config_db #(uvm_reset::config_item)::set(this, "m_reset", "m_config", m_cfg_reset);
56+
// Creation of the reset
57+
m_reset = uvm_reset::agent::type_id::create("m_reset", this);
58+
59+
60+
// Configuration of the m_rx
61+
m_cfg_rx = new;
62+
m_cfg_rx.active = UVM_ACTIVE;
63+
// interface register name has to be same in testbench uvm_config_db#(...)::set();
64+
m_cfg_rx.interface_name = "vif_axi_rx";
65+
uvm_config_db #(uvm_axi::config_item)::set(this, "m_rx", "m_config", m_cfg_rx);
66+
// Creation of the m_rx
67+
m_rx = uvm_axi::agent_rx #(RX_ITEMS, RX_ITEM_WIDTH, TUSER_WIDTH)::type_id::create("m_rx", this);
68+
69+
// Configuration of the m_tx
70+
m_cfg_tx = new;
71+
m_cfg_tx.active = UVM_ACTIVE;
72+
// interface register name has to be same in testbench uvm_config_db#(...)::set();
73+
m_cfg_tx.interface_name = "vif_axi_tx";
74+
uvm_config_db #(uvm_axi::config_item)::set(this, "m_tx", "m_config", m_cfg_tx);
75+
// Creation of the m_tx
76+
m_tx = uvm_axi::agent_tx #(TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH)::type_id::create("m_tx", this);
77+
78+
// Create sequencer
79+
m_sequencer = sequencer #(
80+
RX_ITEMS, RX_ITEM_WIDTH, TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH
81+
)::type_id::create("m_sequencer", this);
82+
83+
m_sc = scoreboard #(TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH)::type_id::create("m_sc", this);
84+
85+
m_model = uvm_vector2packet::model #(
86+
RX_ITEMS, RX_ITEM_WIDTH, TX_ITEMS, TX_ITEM_WIDTH, TUSER_WIDTH
87+
)::type_id::create("m_model", this);
88+
endfunction
89+
90+
// Connect agent's ports with ports from scoreboard.
91+
function void connect_phase(uvm_phase phase);
92+
// Connection of the reset
93+
//m_reset.sync_connect(m_rx.reset_sync);
94+
//m_reset.sync_connect(m_tx.reset_sync);
95+
96+
// Connection to scoreboard
97+
m_rx.analysis_port.connect(m_model.m_rx.analysis_export);
98+
// Connect to Scoreboard
99+
m_model.m_tx.connect(m_sc.cmp.analysis_imp_model);
100+
m_tx.analysis_port.connect(m_sc.cmp.analysis_imp_dut);
101+
102+
// Connect sequencer
103+
m_sequencer.m_reset = m_reset.m_sequencer;
104+
m_sequencer.m_rx = m_rx.m_sequencer;
105+
m_sequencer.m_tx = m_tx.m_sequencer;
106+
endfunction
107+
endclass

0 commit comments

Comments
 (0)