Commit 65e97117 authored by Guillem's avatar Guillem
Browse files

WIP: Doing unit tests

For the TB an AXI-LITE master from verilog.pro has been added
parent c3cb9269
//
// AXI master cycles generator
///
// Design by G.J. van Loo, FenLogic Ltd, October-2017.
//
// This program is free software. It comes without any guarantees or
// warranty to the extent permitted by applicable law. Although the
// author has attempted to find and correct any bugs in this free software
// program, the author is not responsible for any damage or losses of any
// kind caused by the use or misuse of the program. You can redistribute
// the program and or modify it in any form without obligations, but the
// author would appreciated if these credits stays in.
//
// This AXI test master has been developed alongside the axi_mux code.
// As with most big test benches I had to balance the time spend on
// the test bench versus the actual code being tested. Therefore this
// test bench is not complete:
// No support for user signals
// No support for response delays
// No support for write response matching
// Not checking the reponse IDs
//
// For operating an usage details read the manual.
//
`timescale 1 ns / 1 ps
module axi_test_master
#( parameter ADRS_BITS = 32,
DATA_BITS = 32,
LEN_BITS = 4,
ID_BITS = 6,
// Depth of the queues (FIFOs)
AQ_DEPTH = 10, // Max 1K addresses
DQ_DEPTH = 16 // Max 64K data
)
(
input clk,
input reset_n,
input run,
output done,
//
// Write ports
//
// Write address
output reg awvalid ,
input awready ,
output [ADRS_BITS-1:0] awaddr ,
output [LEN_BITS-1:0] awlen ,
output [ 2:0] awsize ,
output [ID_BITS-1:0] awid ,
output [ 1:0] awburst ,
// Unsupported ports (0)
output [ 3:0] awcache ,
output [ 1:0] awlock ,
output [ 2:0] awprot ,
output [ 3:0] awqos ,
// write data
output reg wvalid ,
input wready ,
output [DATA_BITS-1:0] wdata ,
output [DATA_BITS/8-1:0] wstrb ,
output wlast ,
output [ID_BITS-1:0] wid ,
// write response
input bvalid ,
output bready ,
input [ 1 :0] bresp ,
input [ID_BITS-1:0] bid ,
//
// Read ports
//
// Read address
output reg arvalid,
input arready,
output [ADRS_BITS-1:0] araddr ,
output [ID_BITS-1:0] arid ,
output [LEN_BITS-1:0] arlen ,
output [2:0] arsize ,
output [1:0] arburst,
// Unsupported ports (0)
output [3:0] arcache,
output [1:0] arlock ,
output [2:0] arprot ,
output [3:0] arqos ,
// Read Data
input rvalid ,
output rready ,
input [DATA_BITS-1:0] rdata ,
input rlast ,
input [ID_BITS-1 :0] rid ,
input [ 1 :0] rresp
);
assign awcache = 4'b0;
assign awlock = 2'b0;
assign awprot = 3'b0;
assign awqos = 4'b0;
// Not checking bresponse ID/code yet
assign bready = 1'b1;
// read response id is checked
// use axi_test_master.rid_check_on=0 to disable (but why should you?)
reg rid_check_on = 1'b1;
/***********************************************************\
* _ _ _ _ ___ _ *
* | | | | (_) | / _ \ | | *
* | | | |_ __ _| |_ ___ / /_\ \ __| |_ __ ___ *
* | |/\| | '__| | __/ _ \ | _ |/ _` | '__/ __| *
* \ /\ / | | | || __/ | | | | (_| | | \__ \ *
* \/ \/|_| |_|\__\___| \_| |_/\__,_|_| |___/ *
* *
\***********************************************************/
// Write address FIFO holds:
// awaddr : ADRS_BITS
// awlen : LEN_SIZE
// awsize : 3
// awid : ID_BITS
// awburst: 2
//
// Omitted (tied off) are:
// awcache: 4
// awlock : 2
// awprot : 3
// awqos : 4
localparam AW_WIDTH = ADRS_BITS+LEN_BITS+3+ID_BITS+2;
reg awff_write;
reg [AW_WIDTH-1:0] awff_wdata;
wire awff_read;
wire [AW_WIDTH-1:0] awff_rdata;
wire awff_full;
wire awff_empty;
wire [AQ_DEPTH:0] awff_level;
sync_fifo
#(.WIDTH(AW_WIDTH), // width in bits
.L2D(AQ_DEPTH), // Log 2 Depth, 5=32 deep
.REGFLAGS(0) // Full, empty are registered
)
aw_fifo
(
.clk (clk ), // system clock
.reset_n(reset_n ), // A-synchronous low reset/clear
.enable (1'b1 ), // clock gating
.clear (1'b0 ), // Synchronous clear
.write (awff_write), // write FIFO
.wdata (awff_wdata), // write data
.read (awff_read ), // read FIFO
.rdata (awff_rdata), // read data
.empty (awff_empty), // FIFO is empty
.full (awff_full ), // FIFO is full
.level (awff_level) // Fill level
);
// Split AW fifo output in fields
assign {
awaddr ,
awlen ,
awsize ,
awid ,
awburst
} = awff_rdata;
//
// Delay & sync have their own FIFO
// as we need it one cycle ahead of the axi data
//
reg [8:0] awdff_wdata;
wire awdff_read;
wire [8:0] awdff_rdata;
wire awdff_empty;
wire [ 7:0] aw_delay;
wire aw_sync;
reg aw_wait_sync;
// These belong to the write data control but
// are used in address FSM below
wire w_sync;
wire wff_empty;
reg w_wait_sync;
wire wdff_empty;
//
sync_fifo
#(.WIDTH(9), // width in bits
.L2D(AQ_DEPTH), // Log 2 Depth, 5=32 deep
.REGFLAGS(0) // Full, empty are registered
)
aw_delay_fifo
(
.clk (clk ), // system clock
.reset_n(reset_n ), // A-synchronous low reset/clear
.enable (1'b1 ), // clock gating
.clear (1'b0 ), // Synchronous clear
.write (awff_write), // write FIFO
.wdata (awdff_wdata), // write data
.read (awdff_read ), // read FIFO
.rdata (awdff_rdata), // read data
.empty (awdff_empty), // FIFO is empty
.full (), // FIFO is full
.level () // Fill level
);
assign {aw_sync,aw_delay} = awdff_rdata;
//
// Put a write address in the FIFO
//
task q_wadrs;
input integer address;
input integer length;
input integer size;
input integer id;
input integer burst;
input integer delay_random;
input integer delay;
reg [7:0] out_delay;
begin
// If full and not running risc of blocking is great
// (unless somebosy used a fork)
if (awff_full)
begin
if (!run)
begin
$display("%m: AW FIFO is full. Stopping...");
#1 $stop;
end
else
begin
while (awff_full)
@(posedge clk);
end
end
if (delay_random)
out_delay = $random % delay;
else
out_delay = delay;
// Convert size if so required
// Everything below 8 is assumed to have the
// real AXI format already
case (size)
8 : size = 0;
16 : size = 1;
32 : size = 2;
64 : size = 3;
128 : size = 4;
256 : size = 5;
512 : size = 6;
1024 : size = 7;
default : if (size<0 || size>7)
begin
$display("%m: Illegal size. Stopping...");
$stop;
end
endcase
// Convert from user to AXI length
length = length-1;
awff_wdata = {address[ADRS_BITS-1:0],
length[LEN_BITS-1:0],
size[2:0],
id[ID_BITS-1:0],
burst[1:0]};
awdff_wdata = {1'b0,out_delay};
@(negedge clk)
awff_write <= 1'b1;
@(posedge clk)
#1 awff_write <= 1'b0;
end
endtask
reg [7:0] aw_delay_count;
always @(posedge clk or negedge reset_n)
begin
if (!reset_n)
begin
awvalid <= 1'b0;
aw_delay_count <= 8'h00;
aw_wait_sync <= 1'b0;
end
else
begin
if (run)
begin
// Are we running a cycle?
if (awvalid)
begin
// Does it finish?
if (awready)
begin
// check FIFO what to do next
if (awff_level==1) // or awdff_empty...
// We are using the bottom entry: stop
awvalid <= 1'b0; // Nothing
else
begin
// FIFO output is valid
aw_delay_count <= aw_delay;
// Do we have a synced cycle?
if (aw_sync)
begin
// If write is witing for async
// or write happens to finish AND it has a sync
// we can continue)
if (w_wait_sync | (wvalid & wready & wlast & (!wdff_empty & w_sync)))
begin
if (aw_delay==0)
// Output data now
awvalid <= 1'b1;
else
awvalid <= 1'b0;
end
else
begin
// Wait for write sync cycle
awvalid <= 1'b0;
aw_wait_sync <= 1'b1;
end
end
else
begin // No sync cyle
if (aw_delay==0)
// Output data now
awvalid <= 1'b1;
else
awvalid <= 1'b0;
end
end // FF has data
end // if awready
// Else continue waiting for ready
end // running cycle
else
begin
// No awvalid,
// Are we waiting for sync?
if (aw_wait_sync)
begin
// Do we have a write sync?
if (wvalid & wready & wlast & (!wdff_empty & w_sync))
begin
aw_wait_sync <= 1'b0;
if (aw_delay==0)
// Output data now
awvalid <= 1'b1;
else
awvalid <= 1'b0;
end
else
if (wff_empty)
begin
$display("%m, AW is waiting for sync that can't come");
#1 $stop;
end
end
else
begin
// Are we running a delay?
if (aw_delay_count!=0)
begin
aw_delay_count <= aw_delay_count - 1;
// Is the delay over?
if (aw_delay_count==1)
awvalid <= 1'b1;
end // running delay
else
begin
// Has the FIFO something?
if (!awff_empty)
begin
aw_delay_count <= aw_delay;
if (aw_delay==0)
// Output data now
awvalid <= 1'b1;
else
awvalid <= 1'b0;
end // nothing in FIFO
end // no delay
end // no sync
end // no valid
end // run
else
awvalid <= 1'b0;
end // clocked
end // always
assign awff_read = awvalid & awready;
// awdff_read is high for every case where awvalid <= 1:
// The following monster expression has been built by
// following the above code and extracting all the paths
// which lead to awvalid <= 1:
assign awdff_read =
run & ((awvalid & awready & (awff_level!=1) &
(
(aw_sync &
(w_wait_sync | (wvalid & wready & wlast & (!wdff_empty & w_sync)))
& (aw_delay==0)
)
|
(!aw_sync & aw_delay==0)
)
) // awvalid
|
(!awvalid &
(
(aw_wait_sync &
(wvalid & wready & wlast & (!wdff_empty & w_sync) &
(aw_delay==0))
) // sync
|
(!aw_wait_sync & ((aw_delay_count==1) |
(!awff_empty& (aw_delay==0)))
) // !sync
)
)
);
// To check the expression awff_read should follow awdff_read
// by one clock cycle if awready is always high
// Repeat all of the above for write data
/********************************************************\
* _ _ _ _ ______ _ *
* | | | | (_) | | _ \ | | *
* | | | |_ __ _| |_ ___ | | | |__ _| |_ __ _ *
* | |/\| | '__| | __/ _ \ | | | / _` | __/ _` | *
* \ /\ / | | | || __/ | |/ / (_| | || (_| | *
* \/ \/|_| |_|\__\___| |___/ \__,_|\__\__,_| *
* *
\********************************************************/
// wdata : DATA_BITS
// wstrb : DATA_BITS/8 bits
// wlast : 1
// wid : ID_BITS
//
// +2 as it also holds sync_mode
localparam W_WIDTH = DATA_BITS+DATA_BITS/8+1+ID_BITS;
reg wff_write;
reg [W_WIDTH-1:0] wff_wdata;
wire wff_read;
wire [W_WIDTH-1:0] wff_rdata;
wire wff_full;
wire [DQ_DEPTH:0] wff_level;
//wire w_sync;
sync_fifo
#(.WIDTH(W_WIDTH), // width in bits
.L2D(DQ_DEPTH), // Log 2 Depth, 5=32 deep
.REGFLAGS(0) // Full, empty are registered
)
w_fifo
(
.clk (clk ), // system clock
.reset_n(reset_n ), // A-synchronous low reset/clear
.enable (1'b1 ), // clock gating
.clear (1'b0 ), // Synchronous clear
.write (wff_write), // write FIFO
.wdata (wff_wdata), // write data
.read (wff_read ), // read FIFO
.rdata (wff_rdata), // read data
.empty (wff_empty), // FIFO is empty
.full (wff_full ), // FIFO is full
.level (wff_level) // Fill level
);
// Split W fifo output in fields
assign {
wdata ,
wstrb ,
wlast ,
wid
} = wff_rdata;
//
// Delay has it's own FIFO
// as we need it one cycle ahead of the axi data
//
reg [8:0] wdff_wdata;
wire wdff_read;
wire [8:0] wdff_rdata;
wire [ 7:0] w_delay;
//
sync_fifo
#(.WIDTH(9), // width in bits
.L2D(DQ_DEPTH), // Log 2 Depth, 5=32 deep
.REGFLAGS(0) // Full, empty are registered
)
w_delay_fifo
(
.clk (clk ), // system clock
.reset_n(reset_n ), // A-synchronous low reset/clear
.enable (1'b1 ), // clock gating
.clear (1'b0 ), // Synchronous clear
.write (wff_write), // write FIFO
.wdata (wdff_wdata), // write data
.read (wdff_read ), // read FIFO
.rdata (wdff_rdata), // read data
.empty (wdff_empty), // FIFO is empty
.full (), // FIFO is full
.level () // Fill level
);
assign {w_sync,w_delay} = wdff_rdata;
//
// Put write data in the FIFO
//
// TODO: Support more then 1 unit?
//
task q_wdata;
input integer data;
input integer strobes;
input integer last;
input integer id;
input integer delay_random;
input integer delay;
reg [7:0] out_delay;
begin
// If full and not running risc of blocking is great
// (unless somebosy used a fork)
if (wff_full)
begin
if (!run)
begin
$display("%m: W FIFO is full. Stopping...");
$stop;
end
else
begin
while (wff_full)
@(posedge clk);
end
end
if (delay_random)
out_delay = $random % delay;
else
out_delay = delay;
wff_wdata = {data[DATA_BITS-1:0],
strobes[DATA_BITS/8-1:0],
last[0],
id[ID_BITS-1:0]}; // sync mode
wdff_wdata = {1'b0,out_delay};
@(negedge clk)
wff_write <= 1'b1;
@(posedge clk)
#1 wff_write <= 1'b0;
end
endtask
reg [7:0] w_delay_count;
always @(posedge clk or negedge reset_n)
begin
if (!reset_n)
begin
wvalid <= 1'b0;
w_delay_count <= 8'h00;
w_wait_sync <= 1'b0;
end
else