// testbench for chan_ctrl.v
`timescale 10ns/1ns
`define HALF_CLK (5.0)
class channel_data;
bit [13:0] base;
bit [19:0] size;
bit [19:0] loop; // actually loop-size
bit [ 5:0] add_int;
bit [11:0] add_frac;
bit [19:0] offset_int;
bit [11:0] offset_frac;
bit surround;
bit loopena;
bit [ 5:0] vol_left;
bit [ 5:0] vol_right;
function void init();
base = $random()>>(32-14);
add_int = $random()>>(32-6);
add_frac = $random()>>(32-12);
offset_int = $random()>>(32-20);
offset_frac = $random()>>(32-12);
size = $random()>>(32-10); // TODO: greater sizes
loop = {20{1'b1}};
while(loop>=size)
loop = $random()>>(32-10); // TODO: greater sizes
surround = $random()>>(32-1);
loopena = $random()>>(32-1);
vol_left = $random()>>(32-6);
vol_right = $random()>>(32-6);
endfunction
function void update();
bit cy;
int new_off;
bit [21:0] addr;
{cy,offset_frac} = {1'b0, offset_frac} + {1'b0, add_frac};
new_off = {31'd0, cy} + {26'd0,add_int} + {12'd0,offset_int};
if( new_off >= size )
begin
new_off = new_off - size + loop;
end
offset_int = new_off[19:0];
endfunction
function int get_word(input int wnum);
// return words suitable for channels state
if( wnum==0 )
get_word = {offset_int,offset_frac};
else if( wnum==1 )
get_word = {add_int, add_frac, loopena, surround, vol_left, vol_right};
else if( wnum==2 )
get_word = { (4'd0|$random()), size, base[7:0] };
else if( wnum==3 )
get_word = { (4'd0|$random()), loop-size, 2'b0, base[13:8] };
else
$stop;
endfunction
function bit [21:0] get_addr();
get_addr = base*256 + offset_int;
endfunction
function bit [6:0] get_vol_left();
get_vol_left = {1'b0, vol_left};
endfunction
function bit [6:0] get_vol_right();
if( surround )
get_vol_right = 7'h7F - {1'b0, vol_right};
else
get_vol_right = {1'b0, vol_right};
endfunction
function bit [7:0] get_frac();
get_frac = offset_frac[11:4];
endfunction
endclass
module tb;
reg clk;
reg rst_n;
// sync counter
integer sync_cnt;
bit pre_sync;
// DUT connections
wire [ 6:0] rd_addr;
tri0 [31:0] rd_data;
wire [ 6:0] wr_addr;
wire [31:0] wr_data;
wire wr_stb;
reg sync_stb;
reg [31:0] ch_enas;
wire [ 7:0] out_data;
wire out_stb_addr;
wire out_stb_mix;
// channels memory
reg [31:0] channels_mem [0:127];
// test data
channel_data chans[0:31];
// generation fifos
reg [7:0] mix_fifo[$];
reg [7:0] addr_fifo[$];
// init tb data structures
initial
begin : chans_create
int i;
for(i=0;i<32;i++) chans[i] = new;
mix_fifo.delete();
addr_fifo.delete();
end
// clock and reset gen
initial
begin
rst_n = 1'b0;
clk = 1'b1;
forever #(`HALF_CLK) clk = ~clk;
end
//
initial
begin
#(1);
repeat (3) @(posedge clk);
rst_n <= 1'b1;
end
// sync generator
initial
begin
sync_cnt = 0;
pre_sync = 1'b0;
sync_stb = 1'b0;
end
//
always @(posedge clk)
if( !rst_n )
begin
sync_cnt <= 637;
pre_sync <= 1'b0;
sync_stb <= 1'b0;
end
else
begin
if( sync_cnt<(640-1) )
sync_cnt <= sync_cnt + 1;
else
sync_cnt <= 0;
pre_sync <= !sync_cnt;
sync_stb <= pre_sync;
end
// channels memory emulator
reg [31:0] rd_data_reg;
assign rd_data = rd_data_reg;
//
always @(posedge clk)
rd_data_reg <= channels_mem[rd_addr];
//
always @(posedge clk)
if( wr_stb )
channels_mem[wr_addr] <= wr_data;
// fill queues off the output data
always @(posedge clk)
if( out_stb_mix ) mix_fifo.push_back(out_data);
//
always @(posedge clk)
if( out_stb_addr ) addr_fifo.push_back(out_data);
// channel generator/checker
always @(posedge clk)
if( sync_stb )
begin : chans
int i;
// if there was previous iteration, check it
// init channels for new iteration
for(i=0;i<32;i++)
begin
chans[i].init();
channels_mem[i*4+0] = chans[i].get_word(0);
channels_mem[i*4+1] = chans[i].get_word(1);
channels_mem[i*4+2] = chans[i].get_word(2);
channels_mem[i*4+3] = chans[i].get_word(3);
chans[i].update();
end
end
always @(posedge clk)
ch_enas = 32'hFFFF_FFFF;
// DUT
chan_ctrl chan_ctrl
(
.clk (clk ),
.rst_n(rst_n),
.rd_addr(rd_addr),
.rd_data(rd_data),
.wr_addr(wr_addr),
.wr_data(wr_data),
.wr_stb (wr_stb ),
.sync_stb(sync_stb),
.ch_enas(ch_enas),
.out_data (out_data ),
.out_stb_addr(out_stb_addr),
.out_stb_mix (out_stb_mix )
);
endmodule