Skip to content

Commit 58beed0

Browse files
committed
added first version of the fabric verification including fifo_arb,mini_core,fabric and fabric_mini_cores.
1 parent 3d3a996 commit 58beed0

File tree

4 files changed

+493
-1
lines changed

4 files changed

+493
-1
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
## Strategy
2+
In order to create a good env for the fabric we seperated the fabric into two conceptual components, the first one is the traffic, this part only checks if the data is moving as expected inside the fabric.
3+
the second part will be a mini_cores_fabric which include also the mini_cores, so the fabric_mini_cores_tb is an extension of the fabric_tb.
4+
- we also have trackers that it is a log that including all the transactions and the time of the transaction sample.
5+
### Code
6+
## fabric_tb
7+
The fabric_tb is created from 3 main parts.
8+
1. Interface - the interface is where we connect from the software to the hardware, we are not using UVM so everything is a bit different then normal. the fabric is a 3x3 mini_core_tile that created using generate inside the fabric. This fact make it very difficult to get a signal in a generic way like signal[col][row] because of the generate. so we created those signals (i.e. interface) using generate.
9+
```systemverilog
10+
genvar row, col;
11+
generate
12+
for (col = 1; col <= V_COL; col = col + 1) begin : gen_col
13+
for (row = 1; row <= V_ROW; row = row + 1) begin : gen_row
14+
// fabric to if
15+
assign fabric.col[col].row[row].mini_core_tile_ins.mini_core_top.mini_mem_wrap.C2F_ReqValidQ103H = valid_tile[col][row]; // input to req_fifo
16+
assign fabric.col[col].row[row].mini_core_tile_ins.mini_core_top.mini_mem_wrap.C2F_ReqQ103H = origin_trans[col][row];
17+
// if to fabric
18+
assign origin_trans_fab[col][row] = fabric.col[col].row[row].mini_core_tile_ins.mini_core_top.mini_mem_wrap.C2F_ReqQ103H; // input_data to req_fifo
19+
assign tile_rsp_trans[col][row] = fabric.col[col].row[row].mini_core_tile_ins.mini_core_top.mini_mem_wrap.F2C_OutFabricQ504H;// input_data to rd_rsp fifo
20+
assign valid_tile_rsp[col][row] = fabric.col[col].row[row].mini_core_tile_ins.mini_core_top.mini_mem_wrap.F2C_OutFabricValidQ504H;// valid input_data to rd_rsp fifo
21+
assign valid_local[col][row] = fabric.col[col].row[row].mini_core_tile_ins.out_local_req_valid;
22+
assign target_trans[col][row] = fabric.col[col].row[row].mini_core_tile_ins.out_local_req;
23+
assign requestor_id_ref[col][row] = fabric.col[col].row[row].mini_core_tile_ins.pre_in_local_req.requestor_id;
24+
assign tile_ready[col][row] = fabric.col[col].row[row].mini_core_tile_ins.out_local_ready;
25+
end
26+
end
27+
endgenerate
28+
```
29+
We created for each signal a 2D array that is connected to its relevant tile.
30+
31+
2. sequence - the sequence is creating the traffic for the fabric, collect the data from each tile and then actiate a DI checker.
32+
- traffic - The data is random but it has more fields, we randomize the source tile and the target tile while making sure that it is not the same tile. then we are randomize the opcode if it is WR or RD. we can read only after write.
33+
34+
- data collection - the data collectors are created like this:
35+
```systemverilog
36+
static t_tile_trans_v monitor_source_trans [V_ROW:1] [V_COL:1] [$];
37+
```
38+
this is a V_ROWxV_COL queue array from t_tile_trans type. each element is collecting the data for a specific tile, in this case as a source tile.
39+
```systemverilog
40+
task automatic fabric_get_source_from_tile();
41+
t_tile_trans_v [V_COL:1][V_ROW:1] temp_trans_req;
42+
t_tile_trans_v [V_COL:1][V_ROW:1] temp_trans_rsp;
43+
for(int i = 1; i<= V_COL; i++) begin
44+
for(int j = 1; j<= V_ROW; j++) begin
45+
automatic int col = i;
46+
automatic int row = j;
47+
fork forever begin
48+
t_tile_id source_id;
49+
wait(valid_tile[col][row] == 1'b1);
50+
source_id[7:4] = col;
51+
source_id[3:0] = row;
52+
#0;
53+
temp_trans_req[col][row].trans.data = origin_trans_fab[col][row].data;
54+
temp_trans_req[col][row].trans.opcode = origin_trans_fab[col][row].opcode;
55+
temp_trans_req[col][row].trans.address = origin_trans_fab[col][row].address;
56+
temp_trans_req[col][row].trans.next_tile_fifo_arb_id = NULL_CARDINAL;
57+
temp_trans_req[col][row].trans.requestor_id = '0;
58+
temp_trans_req[col][row].source = source_id;
59+
temp_trans_req[col][row].target = origin_trans_fab[col][row].address[31:24];
60+
monitor_source_trans[col][row].push_back(temp_trans_req[col][row]);
61+
cnt_trans_source = cnt_trans_source + 1;
62+
wait(valid_tile[col][row] == 1'b0);
63+
end forever begin // RD_RSP
64+
t_tile_id source_id_rsp;
65+
wait(valid_tile_rsp[col][row] == 1'b1);
66+
source_id_rsp[7:4] = col;
67+
source_id_rsp[3:0] = row;
68+
#0;
69+
temp_trans_rsp[col][row].trans.data = '0;
70+
temp_trans_rsp[col][row].trans.address = '0;
71+
temp_trans_rsp[col][row].trans.opcode = tile_rsp_trans[col][row].opcode; // input to fifo of RD_RSP in mem_wrap
72+
temp_trans_rsp[col][row].trans.requestor_id = '0;
73+
temp_trans_rsp[col][row].trans.next_tile_fifo_arb_id = NULL_CARDINAL;
74+
temp_trans_rsp[col][row].source = source_id_rsp;
75+
temp_trans_rsp[col][row].target = tile_rsp_trans[col][row].address[31:24];
76+
monitor_source_trans_rsp[col][row].push_back(temp_trans_rsp[col][row]);
77+
cnt_trans_source_rsp = cnt_trans_source_rsp + 1;
78+
wait(valid_tile_rsp[col][row] == 1'b0);
79+
end
80+
join_none
81+
end
82+
end
83+
```
84+
we have 2 collectors in this env:
85+
- the source collector that collect the data from the source tile it can be a regular data or RD_RSP which is the data that is the data that coming back to the source tile after a read request.
86+
- the target collector is collecting the data that finish its traffic through the fabric.
87+
The collectors wait for the relevant signals to be valid and then collect the data into a queue.
88+
at the end we are activating a DI_checker that checks the data.
89+
90+
3. Tests:
91+
```systemverilog
92+
task run_fabric_test(input string test);
93+
if (test == "fabric_alive") begin
94+
`include "fabric_alive.sv"
95+
end else if(test == "fabric_all_tiles") begin
96+
`include "fabric_all_tiles.sv"
97+
end else if(test == "fabric_wr_rd_data") begin
98+
`include "fabric_wr_rd_data.sv"
99+
end else if(test == "fabric_BP_test") begin
100+
`include "fabric_BP_test.sv"
101+
end else begin
102+
$error(" [ERROR] : test %s not found",test);
103+
end
104+
endtask
105+
```
106+
The 2 main tests are the fabric_all_tiles_test and fabric_BP_test.
107+
The fabric_all_tiles_test is activating all tiles in parallel, it ensure that the fabric is reliable when it has all kind of traffic like stress or very low traffic.
108+
the fabrc_BP_test is a test that creating a lot of pressure on each tile, we fill all the fifo of all tiles in transactions and we want to see how the fabric is handling the pressure, if he decline new transactions or if after the release of the pressure the fabric is handling it correctley.
109+
110+
## fabric_mini_cores_tb
111+
This tb is taking the fabric_tb that test the fabric traffic alone and adding to it the actual mini_cores.
112+
in this part we compile a C program that can run on each one of our mini cores. the traffic is verified like before but now we are checking if the program do what it suppose to.
113+
to do it we created 9 IRAM and DRAM kike this:
114+
```systemverilog
115+
logic [7:0] IMem [V_ROW:1] [V_COL:1] [I_MEM_SIZE_MINI + I_MEM_OFFSET_MINI - 1 : I_MEM_OFFSET_MINI];
116+
logic [7:0] DMem [V_ROW:1] [V_COL:1] [D_MEM_SIZE_MINI + D_MEM_OFFSET_MINI - 1 : D_MEM_OFFSET_MINI];
117+
```
118+
In order to connect them to the design we assigned them by generate like before and then load each one of the core seperatly like this:
119+
```systemverilog
120+
`MAFIA_DFF(IMem, IMem, clk)
121+
task load_mem(input int col, input int row);
122+
$readmemh({"../../../target/fabric/tests/",test_name,"/gcc_files/inst_mem.sv"} , IMem[col][row]);
123+
...
124+
```
125+
this task is reading the i_mem that created from the linker after compiliation.
126+
next we loaded each imem into the relevant mini core like this:
127+
```systemverilog
128+
...
129+
for(int i = 1; i<= V_COL; i++) begin
130+
for(int j = 1; j<= V_ROW; j++) begin
131+
automatic int col = i;
132+
automatic int row = j;
133+
fork begin
134+
load_mem(col,row);
135+
$display("time is %0t for tile [%0d,%0d]",$time,col,row);
136+
end join
137+
...
138+
end
139+
end
140+
```
141+
In this way we can load for each tile a different program to run in parallel to the other tiles.
142+
143+
144+
145+
# TODO - add DRAM explanation and add c tests explanation.
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
## Strategy
2+
We devided our goal into 3 main topics.
3+
1. Creating meaningful sequences that will assure that our design is robust and can handle pressure.
4+
2. Creating reliable checkers.
5+
3. Creatung a lot of robust tests that will push the design to his limits.
6+
Note - we made here an assamption that if the FIFO_arb will be good then the FIFO and the Arbiter will be good as well.
7+
8+
### Code
9+
Our FIFO_arb verification created with the least amount of pre assumptions like size of FIFO, depth of FIFO and even the amount of FIFO's in the FIFO_arb (even though it was not in our design, we wnated to ensure the most robust DUT).
10+
The module of the TB called "router_tb" and it is also contains one basic test for the router.
11+
The important signals and parameters in the TB are:
12+
```systemverilog
13+
parameter V_REQUESTS = 10;
14+
parameter V_FIFO_DEPTH = 4;
15+
parameter V_NUM_FIFO = 4; // number of fifos to exercise in the test (HW is always 4, simulation may stimuli only some of them)
16+
parameter V_NO_BACK_PRESSURE = 0; // used to disable back pressure in the test which will cause a failure in the test
17+
parameter V_MAX_DELAY = 5; // max delay in the test
18+
parameter V_BACK_PRESURE = 10;
19+
logic clk;
20+
logic rst;
21+
static t_tile_trans ref_fifo_Q [3:0][$];
22+
static t_tile_trans ref_outputs_Q [$];
23+
```
24+
We created the fifo_arb inputs and outputs, the inputs are 4 FIFO's (can be less then 4) that are creatd in software (to be used as RM), they are type of dynamic array in SV that called queue.The output is the winner of each transaction. the type of the FIFO's is t_tile_trans.
25+
we also created some parameters that can change by user definition.
26+
The main sequence is:
27+
``` systemverilog
28+
fork
29+
run_fifo_arb_test(test_name);
30+
fifo_arb_get_inputs();
31+
fifo_arb_get_outputs();
32+
join
33+
```
34+
We are activating three tasks in parallel, run the test and two collectors that will collect the input and the output of the FIFO_arb (for each FIFO obviousley).
35+
36+
- run_fifo_arb_test(test_name);
37+
```systemverilog
38+
task run_fifo_arb_test(input string test);
39+
delay(30);
40+
// ====================
41+
// fifo_arb tests:
42+
// ====================
43+
if (test == "fifo_arb_simple") begin
44+
`include "fifo_arb_simple.sv"
45+
end else if(test == "fifo_arb_single_fifo_full_BW")begin
46+
`include "fifo_arb_single_fifo_full_BW.sv"
47+
end else if(test == "fifo_arb_all_fifo_full_BW")begin
48+
`include "fifo_arb_all_fifo_full_BW.sv"
49+
end else if(test == "fifo_arb_Assertion_test")begin
50+
`include "fifo_arb_Assertion_test.sv"
51+
end else if(test == "fifo_arb_back_pressure")begin
52+
`include "fifo_arb_back_pressure.sv"
53+
end else begin
54+
$error(" [ERROR] : test %s not found",test);
55+
end
56+
endtask
57+
58+
```
59+
This task is running the relevant test that was chosen by the user.
60+
61+
- fifo_arb_get_inputs();
62+
```systemverilog
63+
task automatic fifo_arb_get_inputs();
64+
for(int i = 0; i<4; i++) begin
65+
automatic int index = i;
66+
fork begin
67+
forever begin
68+
wait(valid_alloc_req[index] == 1'b1);
69+
#0;
70+
cnt_in = cnt_in + 1;
71+
$display("input of fifo number %0d and CNT_IN = %0d",index,cnt_in);
72+
ref_fifo_Q[index].push_back(alloc_req[index]);
73+
wait(valid_alloc_req[index] == 1'b0);
74+
end
75+
end
76+
forever begin
77+
wait(fifo_arb_ins.arb.winner_dec_id[index] == 1'b1);
78+
if(winner_req_valid == 1'b0) $display("problem in fifo %0d at time %0t",index,$time);
79+
cnt_fifo_pop = cnt_fifo_pop + 1;
80+
$display("cnt_fifo_pop = %0d in fifo %0d at time %0t winner_valid is %0b and fifo_pop is %4b",cnt_fifo_pop,index,$time,winner_req_valid,fifo_arb_ins.arb.winner_dec_id);
81+
wait(fifo_arb_ins.arb.winner_dec_id[index] == 1'b0);
82+
end
83+
join_none
84+
end
85+
endtask
86+
```
87+
This task is using a technique that creating all the fifo's at the same time in parallel, we are using fork - join_none to put the process in the background so the code will continue to run. in this way we created a for loop that generates 4 software fifo's in parallel.
88+
Each FIFO is getting a thread of his own that including two sub threds that runs in parallel as well.The first sub thread is collecting all the transaction inputs of the relevant FIFO, the second sub thread is collecting the output of each FIFO, this way we can know where was an issue in a more specific way.
89+
90+
- fifo_arb_get_outputs();
91+
```systemverilog
92+
task automatic fifo_arb_get_outputs();
93+
int fifo_pop_cnt = 0;
94+
fork
95+
forever begin
96+
@(winner_req);
97+
#0;
98+
if(winner_req_valid == 1'b1)begin
99+
cnt_out = cnt_out + 1;
100+
ref_outputs_Q.push_back(winner_req);
101+
$display("CNT OUT = %0d",cnt_out);
102+
end
103+
end
104+
join_none
105+
endtask
106+
```
107+
This task, also run's in the bacground, simply collect all the output transactions from the fifo_arb and save them in a queue for post process.
108+
109+
- Post process.
110+
The last part of the verification is the DI_checker (Data Integrity).
111+
after the sequence is finished we are activating a checker that will compare the data from the inputs and the output.
112+
113+
```systemverilog
114+
task fifo_arb_DI_checker(); // pseudo SB
115+
automatic bit check = 0;
116+
repeat(5000)begin
117+
foreach(ref_fifo_Q[i,j])begin
118+
foreach(ref_outputs_Q[k])begin
119+
if(ref_fifo_Q[i][j] == ref_outputs_Q[k])begin
120+
ref_fifo_Q[i].delete(j);
121+
ref_outputs_Q.delete(k);
122+
end
123+
end
124+
end
125+
end
126+
if(ref_outputs_Q.size()!= 0)begin
127+
$error("output list not empty ,data is and size %0d",ref_outputs_Q.size());
128+
check = 1'b1;
129+
end
130+
for(int i=0;i<4;i++)begin
131+
if(ref_fifo_Q[i].size() != 0)begin
132+
check = 1'b1;
133+
$error("input list not empty for fifo %0d , and size %0d",i,ref_fifo_Q[i].size());
134+
end
135+
end
136+
if(check == 1'b0)
137+
$display("DI CHECKER: DATA IS CORRECT");
138+
endtask
139+
```
140+
This task is simply checks if all the inputs did got out from the fifo_arb. it iterates all the arrays and compare the input array to the output array, if there is a mismatch then the checker will allert in which fifo and whuch transaction we had this issue.
141+
142+
# Tests
143+
We wanted to create the strongest tests that will push our design to its limits.
144+
We have 5 tests that can be paramtrize.
145+
1. fifo_arb_simple.
146+
2. fifo_arb_single_fifo_full_BW.
147+
3. fifo_arb_all_fifo_full_BW.
148+
4. fifo_arb_Assertion_test.
149+
5. fifo_arb_back_pressure.
150+
151+
Tests 1 and 2 are very simple and created as first step just to see the flow of the design.
152+
153+
Test 3 is a very powerfull test, it activating all fifo's in the same time and pushes random data in random times to each one of them.
154+
```systemverilog
155+
int cycle_delay;
156+
int cycle_delay_arb;
157+
int delay_test;
158+
static int fifo_finish;
159+
for(int i = 0; i<V_NUM_FIFO; i++) begin
160+
automatic int fifo = i;
161+
fork begin
162+
$display("this is fifo %d at time %t",fifo,$time);
163+
for(int j = 0; j < V_REQUESTS; j++)begin
164+
wait(fifo_arb_ins.full[fifo] == '0);
165+
cycle_delay = $urandom_range(0, V_MAX_DELAY);
166+
delay(cycle_delay);
167+
$display("fifo %d and request %0d at time: %0t and full[%0d] is %0b and full is %4b",fifo,j,$time,fifo,fifo_arb_ins.full[fifo],fifo_arb_ins.full );
168+
fifo_arb_gen_trans(fifo);
169+
end
170+
fifo_finish = fifo_finish + 1;
171+
$display("############# this is FIFO_FINISH %0d in fifo %0d at time %t",fifo_finish,fifo,$time);
172+
173+
end
174+
join_none
175+
end
176+
fork : in_ready_arb_fifo_fork
177+
begin
178+
forever begin
179+
cycle_delay_arb = $urandom_range(2, V_MAX_DELAY + 15);
180+
rand_in_ready = $urandom_range(0,15);
181+
delay(cycle_delay_arb);
182+
in_ready_arb_fifo = {5{rand_in_ready}};
183+
end
184+
end
185+
begin
186+
delay_test = V_REQUESTS/V_NUM_FIFO;
187+
delay(delay_test);
188+
end
189+
join_any
190+
disable in_ready_arb_fifo_fork;
191+
in_ready_arb_fifo = 5'b11111;
192+
wait(fifo_finish == (V_NUM_FIFO));
193+
$display("############# this is empty %4b at time %t befote wait",fifo_arb_ins.empty,$time);
194+
wait(fifo_arb_ins.empty == 4'b1111);
195+
$display("############# after wait FIFO_FINISH %0d at time %0t",fifo_finish,$time);
196+
$display("############# this is empty %4b at time %t after wait",fifo_arb_ins.empty,$time);
197+
```
198+
This test is activating all FIFO's using fork - join_none and randomize the data and the delay.
199+
the number of transaction is defined by the user as long as the number of FIFO's and the depth of each FIFO. We created a lot of tests that changes those parameters.
200+
201+
The assertion_test is not in the GK, it is only a test that will violate the rule of our assertion so we can verify that out assertions are correct.
202+
203+
and the last test is a back_pressure_test. In this test we wanted to check a real scenario that the fifo_arb will need to handle with. in this case we fill the fifo_arb completley and we didnt allow the transaction to get out of the fifo_arb. we expect that the fifo_arb wont take any new request until the pressure is down. we did it by blocking (using XMR) the ready signal that get into the fifo_arb so the transactions wont be able to get out.
204+
205+
# Conclusion
206+
- our sequence is a generic one that all our tests are using it.
207+
- our DI checker is a simple yet a powerfull one, we found a lot of issues that were fixed and it assure us a reliable GK.
208+
In order to verify smaller parts or protocols we added assertions and not a checkers, for instanse we use an assertion that checks if we are trying to read from an empty FIFO, our assertion_test verify that.
209+
- Those tests are very robust and we changed the parameter to get into lot of corners in our design.
210+
- All in all this env helped us to find a lot of bugs and issues that were in the origin design and it gave us some confident about the reliability of our design.
211+
212+
213+

docs/fabric/verification/verification_intro.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,17 @@
22
sidebar_position: 1
33
---
44

5-
# Fabric Intro
5+
# Fabric Verification Intro
6+
This chapter will describe the verification agenda of the Fabric.
7+
- In the fabric we have verified three main components - FIFO_arb, Fabric and Mini_core_tile.
8+
- Each one of those components have its own uniqe enviroment that include TB, flow tasks, verification tasks and test lists.
9+
10+
## Terms that will be used.
11+
- sequence - a sequence is a flow of activating a DUT.
12+
- test - a test is a scenario that we want to check, a test can contain more then one sequence.
13+
- TB - test bench - will activate our DUT and will connect it to the verification enviroment.
14+
- Checkers - objects or components that will ensure the reliabilty of our design like data integrity checker and protocol checkers etc.
15+
- RM - reference model - a software object that will calculate the expected output of a DUT for each transaction that the DUT is getting.
16+
- fork join/join_any/join_none - a fork will create a number of threads that will run in parallel. The ending can be join i.e exit the fork only when all of the threads are over. join_any i.e exit the fork when one or more threads done. join_none - exit the fork even if no thred is done.
17+
618

0 commit comments

Comments
 (0)