I am building a timer ip project.
`include "register_control.sv"
`include "logic_control.sv"
`include "counter.sv"
module timer #(parameter ADDR_WIDTH = 8,
parameter DATA_WIDTH = 8,
parameter WAIT = 0
)
(pclk, presetn, psel, penable, pwrite, paddr, pwdata, prdata, pready, pslverr, s_ovf, s_udf, cnt, tdr_reg, tcr_reg, tsr_reg, clk_in1, clk_in2, clk_in3, clk_in4);
input wire pclk;
input wire presetn;
input wire psel;
input wire penable;
input wire pwrite;
input wire [ADDR_WIDTH-1:0] paddr;
input wire [DATA_WIDTH-1:0] pwdata;
output reg [DATA_WIDTH-1:0] prdata;
output reg pready;
output reg pslverr;
output reg s_ovf;
output reg s_udf;
output reg [DATA_WIDTH-1:0] cnt;
input wire clk_in1;
input wire clk_in2;
input wire clk_in3;
input wire clk_in4;
output reg [DATA_WIDTH-1:0] tdr_reg;
output reg [DATA_WIDTH-1:0] tcr_reg;
output reg [DATA_WIDTH-1:0] tsr_reg;
wire clk_in;
wire up_down;
wire enable;
wire load_tdr;
register_control #(.WAIT(WAIT)) register_control_inst (
.pclk (pclk),
.presetn (presetn),
.psel (psel),
.penable (penable),
.pwrite (pwrite),
.paddr (paddr),
.pwdata (pwdata),
.prdata (prdata),
.pready (pready),
.pslverr (pslverr),
.s_ovf (s_ovf),
.s_udf (s_udf),
.tdr_reg (tdr_reg),
.tcr_reg (tcr_reg),
.tsr_reg (tsr_reg)
);
logic_control logic_control_inst (
.clk_in1 (clk_in1),
.clk_in2 (clk_in2),
.clk_in3 (clk_in3),
.clk_in4 (clk_in4),
.tcr_reg (tcr_reg),
.clk_in (clk_in),
.up_down (up_down),
.enable (enable),
.load_tdr (load_tdr)
);
counter counter_inst (
.pclk (pclk),
.presetn (presetn),
.clk_in (clk_in),
.up_down (up_down),
.tdr_reg (tdr_reg),
.enable (enable),
.load_tdr (load_tdr),
.cnt (cnt),
.s_ovf (s_ovf),
.s_udf (s_udf)
);
endmodule
// Code your design here
module register_control #(parameter ADDR_WIDTH = 8,
parameter DATA_WIDTH = 8,
parameter WAIT = 0
)
(pclk, presetn, psel, penable, pwrite, paddr, pwdata, prdata, pready, pslverr, tdr_reg, tcr_reg, tsr_reg, s_ovf, s_udf);
input wire pclk;
input wire presetn;
input wire psel;
input wire penable;
input wire pwrite;
input wire [ADDR_WIDTH-1:0] paddr;
input wire [DATA_WIDTH-1:0] pwdata;
output reg [DATA_WIDTH-1:0] prdata;
output reg pready;
output reg pslverr;
input wire s_ovf;
input wire s_udf;
output reg [DATA_WIDTH-1:0] tdr_reg;
output reg [DATA_WIDTH-1:0] tcr_reg;
output reg [DATA_WIDTH-1:0] tsr_reg;
localparam IDLE = 2'b00,
SETUP = 2'b01,
ACCESS = 2'b10;
reg [7:0] wait_signal;
reg [1:0] current_state;
reg [1:0] next_state;
always @(*) begin
case(current_state)
IDLE: begin
wait_signal = WAIT + 1;
pready = 1'b0;
pslverr = 1'b0;
if(psel & ~penable) begin
next_state = SETUP;
end else begin
next_state = IDLE;
end
end
SETUP: begin
if(psel & penable) begin
if(wait_signal == 0) begin
pready = 1'b1;
if(paddr > 8'h02) begin
pslverr = 1'b1;
end
next_state = IDLE;
end else begin
next_state = ACCESS;
end
end else begin
next_state = SETUP;
end
end
ACCESS: begin
if(wait_signal != 0) begin
next_state = ACCESS;
end else begin
pready = 1'b1;
if(paddr > 8'h02) begin
pslverr = 1'b1;
end
next_state = IDLE;
end
end
default:
next_state = IDLE;
endcase
end
always @(posedge pclk or negedge presetn) begin
if(~presetn) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
wait_signal <= wait_signal - 1;
end
end
always @(posedge pclk or negedge presetn) begin
if(~presetn) begin
prdata <= 8'h00;
tdr_reg <= 8'h00;
tcr_reg <= 8'h00;
tsr_reg <= 8'h00;
end else begin
// write transaction
if((wait_signal == 1) & psel & penable & pwrite) begin
case(paddr)
8'h00:
tdr_reg <= pwdata;
8'h01: begin
tcr_reg[7] <= pwdata[7];
tcr_reg[6] <= 1'b0;
tcr_reg[5:4] <= pwdata[5:4];
tcr_reg[3:2] <= 2'b00;
tcr_reg[1:0] <= pwdata[1:0];
end
8'h02: begin
if((tsr_reg[0] & tsr_reg[1]) & (pwdata[0] & pwdata[1])) begin
tsr_reg[1:0] <= 2'b00;
end else begin
tsr_reg[1:0] <= tsr_reg[1:0];
end
tsr_reg[DATA_WIDTH-1:2] <= 0;
end
default: $display("INVALID ADDRESS");
endcase
// configure tdr register
// tdr_reg <= (paddr == 8'h00) ? pwdata : tdr_reg;
// // configure tcr register
// if(paddr == 8'h01) begin
// tcr_reg[7] <= pwdata[7];
// tcr_reg[6] <= 1'b0;
// tcr_reg[5:4] <= pwdata[5:4];
// tcr_reg[3:2] <= 2'b00;
// tcr_reg[1:0] <= pwdata[1:0];
// end
// // configure tsr register
// if(paddr == 8'h02) begin
// // if(tsr_reg[1] && pwdata[1]) tsr_reg[1] <= 0;
// // else if (tsr_reg[0] && pwdata[0]) tsr_reg[0] <= 0;
// // else begin
// // tsr_reg[0] <= tsr_reg[0];
// // tsr_reg[1] <= tsr_reg[1];
// // end
// // tsr_reg[DATA_WIDTH-1:2] = pwdata[DATA_WIDTH-1:2];
// // if((tsr_reg[0] & tsr_reg[1]) & (~pwdata[0] & ~pwdata[1])) begin
// // tsr_reg[1:0] <= 2'b11;
// // end else begin
// // tsr_reg[1:0] <= 2'b00;
// // end
// // Nếu chỉ có 1 cặp tsr và pwdata == 1 thì sao? Cách bên dưới vẫn sai
// if((tsr_reg[0] & tsr_reg[1]) & (pwdata[0] & pwdata[1])) begin
// tsr_reg[1:0] <= 2'b00;
// end else begin
// tsr_reg[1:0] <= tsr_reg[1:0];
// end
// tsr_reg[DATA_WIDTH-1:2] <= 0;
// end
// read transaction
end else if((wait_signal == 1) & psel & penable & ~pwrite) begin
case(paddr)
8'h00: prdata <= tdr_reg;
8'h01: prdata <= tcr_reg;
8'h02: prdata <= tsr_reg;
default: $display("CANNOT READ DATA FROM INVALID ADDRESS");
endcase
end else begin
tdr_reg <= tdr_reg;
tcr_reg <= tcr_reg;
tsr_reg <= tsr_reg;
end
end
end
always @(*) begin
if (~tsr_reg[0] & s_ovf) begin
tsr_reg[0] = 1'b1;
end else begin
tsr_reg[0] = tsr_reg[0];
end
end
always @(*) begin
if (~tsr_reg[1] & s_udf) begin
tsr_reg[1] = 1'b1;
end else begin
tsr_reg[1] = tsr_reg[1];
end
end
endmodule
// Code your design here
module logic_control #(parameter DATA_WIDTH = 8)(clk_in1, clk_in2, clk_in3, clk_in4, tcr_reg, clk_in, up_down, enable, load_tdr);
input wire clk_in1;
input wire clk_in2;
input wire clk_in3;
input wire clk_in4;
input wire [DATA_WIDTH-1:0] tcr_reg;
output reg clk_in;
output reg up_down;
output reg enable;
output reg load_tdr;
reg [1:0] sel;
always @(*) begin
sel = tcr_reg[1:0];
// $display("sel is %0b", sel);
case(sel)
2'b00: clk_in = clk_in1;
2'b01: clk_in = clk_in2;
2'b10: clk_in = clk_in3;
2'b11: clk_in = clk_in4;
default: $display("Invalid tcr_reg[1:0]!!!");
endcase
// $display("clk_in is %0d", clk_in);
up_down = tcr_reg[5];
enable = tcr_reg[4];
load_tdr = tcr_reg[7];
// $display("up_down is %0d", up_down);
// $display("enable is %0d", enable);
// $display("load_tdr is %0d", load_tdr);
end
endmodule
module counter #(parameter DATA_WIDTH = 8) (pclk, presetn, clk_in, up_down, tdr_reg, enable, load_tdr, cnt, s_ovf, s_udf);
input wire pclk;
input wire presetn;
input wire clk_in;
input wire up_down;
input wire [DATA_WIDTH-1:0] tdr_reg;
input wire enable;
input wire load_tdr;
output reg [DATA_WIDTH-1:0] cnt;
output reg s_ovf;
output reg s_udf;
reg delay_clk_in;
wire posedge_detector;
integer count = 0;
always @(*) begin
if(load_tdr && count == 0) begin
// $display("combinational 1st cnt: %0d at %0t", cnt, $time);
cnt = tdr_reg;
count = count + 1;
// $display("combinational 2nd cnt: %0d at %0t", cnt, $time);
end else begin
cnt = cnt;
count = 0;
// $display("combinational 3rd cnt: %0d at %0t", cnt, $time);
end
end
always @(posedge pclk or negedge presetn) begin
if(~presetn) begin
cnt <= 8'h00;
end else begin
if(enable) begin
if(up_down && posedge_detector) begin
cnt <= cnt + 8'h01;
// $display("sequential cnt: %0d at %0t", cnt, $time);
end else if(~up_down && posedge_detector) begin
cnt <= cnt - 8'h01;
// $display("sequential cnt: %0d at %0t", cnt, $time);
end
end else begin
cnt <= cnt;
end
end
end
always @(posedge pclk or negedge presetn) begin
if(~presetn) begin
s_udf <= 0;
s_ovf <= 0;
end else begin
if(enable) begin
if(~up_down && (cnt == 8'hff))
s_udf <= 1;
else if(up_down && (cnt == 8'h00))
s_ovf <= 1;
else begin
s_udf <= s_udf;
s_ovf <= s_ovf;
end
end else begin
s_udf <= s_udf;
s_ovf <= s_ovf;
end
end
end
// posedge detector block
always @(posedge pclk or negedge presetn) begin
delay_clk_in <= clk_in;
end
assign posedge_detector = clk_in & ~delay_clk_in;
///
endmodule
// Code your testbench here
// or browse Examples
// TESTCASE 1
// `define tdr_test
// TESTCASE 2
// `define tcr_test
// TESTCASE 3
// `define tsr_test
// TESTCASE 4
// `define null_address
// TESTCASE 5
// `define mixed_address
// TESTCASE 6
`define countup_forkjoin_pclk2
// TESTCASE 7
// `define countup_forkjoin_pclk4
// TESTCASE 8
// `define countup_forkjoin_pclk8
// TESTCASE 9
// `define countup_forkjoin_pclk16
// TESTCASE 14
// `define countup_pause_countup_pclk2
// TESTCASE 15
// `define countdw_pause_countdw_pclk2
// TESTCASE 16
// `define countup_reset_countdw_pclk2
// TESTCASE 17
// `define countdw_reset_countup_pclk2
// TESTCASE 18
// `define countup_reset_load_countdw_pclk2
// TESTCASE 19
// `define countdw_reset_load_countdw_pclk2
// TESTCASE 20
// `define fake_underflow
// TESTCASE 21
// `define fake_overflow
module tb_timer;
parameter ADDR_WIDTH = 8;
parameter DATA_WIDTH = 8;
parameter WAIT = 1;
reg pclk;
reg presetn;
reg psel;
reg penable;
reg pwrite;
reg [ADDR_WIDTH-1:0] paddr;
reg [DATA_WIDTH-1:0] pwdata;
wire [DATA_WIDTH-1:0] prdata;
wire pready;
wire pslverr;
wire s_ovf;
wire s_udf;
wire [DATA_WIDTH-1:0] cnt;
wire [DATA_WIDTH-1:0] tdr_reg;
wire [DATA_WIDTH-1:0] tcr_reg;
wire [DATA_WIDTH-1:0] tsr_reg;
reg clk_in1;
reg clk_in2;
reg clk_in3;
reg clk_in4;
integer count;
integer i;
timer #(.WAIT(WAIT)) dut (
.pclk (pclk),
.presetn (presetn),
.psel (psel),
.penable (penable),
.pwrite (pwrite),
.paddr (paddr),
.pwdata (pwdata),
.prdata (prdata),
.pready (pready),
.pslverr (pslverr),
.s_ovf (s_ovf),
.s_udf (s_udf),
.cnt (cnt),
.tdr_reg (tdr_reg),
.tcr_reg (tcr_reg),
.tsr_reg (tsr_reg),
.clk_in1 (clk_in1),
.clk_in2 (clk_in2),
.clk_in3 (clk_in3),
.clk_in4 (clk_in4)
);
task apb_write;
input [ADDR_WIDTH-1:0] addr;
input [DATA_WIDTH-1:0] data_in;
begin
@(posedge pclk);
psel <= 1;
paddr <= addr;
pwdata <= data_in;
pwrite <= 1;
@(posedge pclk);
penable <= 1;
wait(pready);
@(posedge pclk);
psel <= 0;
penable <= 0;
pwrite <= 0;
end
endtask
task apb_read;
input [ADDR_WIDTH-1:0] addr;
begin
@(posedge pclk);
psel <= 1;
paddr <= addr;
pwrite <= 0;
@(posedge pclk);
penable <= 1;
wait(pready);
@(posedge pclk);
psel <= 0;
penable <= 0;
end
endtask
// clock generator
initial begin
pclk = 0;
clk_in1 = 0;
clk_in2 = 0;
clk_in3 = 0;
clk_in4 = 0;
count = 0;
end
always #5 pclk = ~pclk;
always @(posedge pclk) begin
count <= count + 1;
if (count % 1 == 0) clk_in1 <= ~clk_in1;
if (count % 2 == 0) clk_in2 <= ~clk_in2;
if (count % 4 == 0) clk_in3 <= ~clk_in3;
if (count % 8 == 0) clk_in4 <= ~clk_in4;
end
/////////////////
initial begin
presetn = 0;
psel = 0;
penable = 0;
pwrite = 0;
paddr = 0;
pwdata = 0;
// test presetn signal
#20;
presetn = 1;
// TESTCASE 1
`ifdef tdr_test
for(i = 0; i < 20; i = i + 1) begin
$display("TEST %0d", i);
// READ TDR --> CHECK DEFAULT VALUE
apb_read(8'h00);
// WRITE RANDOM VALUE TO TDR
apb_write(8'h00, i + 4);
// READ TDR
apb_read(8'h00);
// COMPARE WRITTEN VALUE
if(prdata == (i + 4))
$display("READ WRITE COMPARISON --> TRUE");
else
$display("READ WRITE COMPARISON --> FALSE");
end
`endif
// TESTCASE 2
`ifdef tcr_test
for(i = 0; i < 20; i = i + 1) begin
$display("TEST %0d", i);
// READ TCR --> CHECK DEFAULT VALUE
apb_read(8'h01);
// WRITE RANDOM VALUE TO TCR
apb_write(8'h01, 5 * i + 2);
// READ TCR
apb_read(8'h01);
$display("prdata is %0d", prdata);
$display("(5 * i + 2) & 10110011 is %0d", (5 * i + 2) & 8'b10110011);
// COMPARE WRITTEN VALUE WITH MASK = 1011_0011
if(prdata == ((5 * i + 2) & 8'b10110011))
$display("READ WRITE COMPARISON --> TRUE");
else
$display("READ WRITE COMPARISON --> FALSE");
end
`endif
// TESTCASE 3
`ifdef tsr_test
for(i = 0; i < 20; i = i + 1) begin
$display("TEST %0d", i);
// READ TSR --> CHECK DEFAULT VALUE
apb_read(8'h02);
// WRITE RANDOM VALUE TO TSR
apb_write(8'h02, 2 * i + 1);
// READ TSR
apb_read(8'h02);
// COMPARE WRITTEN DATA FROM TSR TO 0
if(prdata == 0)
$display("READ WRITE COMPARISON --> TRUE");
else
$display("READ WRITE COMPARISON --> FALSE");
end
`endif
// TESTCASE 4
`ifdef null_address
for(i = 0; i < 20; i = i + 1) begin
$display("TEST %0d", i);
// WRITE A RANDOM VALUE TO A RANDOM ADDRESS
apb_write($urandom_range(3, 255), $random);
$display("pwdata %0b, paddr 8'h%0h", pwdata, paddr);
if(pslverr)
$display("PSLVERR TRIGGERED");
else
$display("PSLVERR NOT TRIGGERED");
// READ DATA FROM RANDOM ADDRESS
apb_read(tb_timer.dut.register_control_inst.paddr);
end
`endif
// TESTCASE 5
`ifdef mixed_address
for(i = 0; i < 20; i = i + 1) begin
$display("TEST %0d", i);
// WRITE A RANDOM VALUE TO A RANDOM ADDRESS
apb_write($random, $random);
$display("pwdata %0b, paddr 8'h%0h", pwdata, paddr);
if(pslverr)
$display("PSLVERR TRIGGERED");
else
$display("PSLVERR NOT TRIGGERED");
if((tb_timer.dut.register_control_inst.paddr != 8'h00) &
(tb_timer.dut.register_control_inst.paddr != 8'h01) &
(tb_timer.dut.register_control_inst.paddr != 8'h02)
) begin
$display("NULL-ADDRESS");
// $display("time %0t", $time);
end
else begin
// READ DATA FROM REGISTER
apb_read(tb_timer.dut.register_control_inst.paddr);
// $display("time %0t", $time);
if(tb_timer.dut.register_control_inst.paddr == 8'h00) begin
if(prdata == tb_timer.dut.register_control_inst.pwdata)
$display("READ WRITE COMPARISON TDR --> TRUE");
else
$display("READ WRITE COMPARISON TDR --> FALSE");
end else if (tb_timer.dut.register_control_inst.paddr == 8'h01) begin
if(prdata == (tb_timer.dut.register_control_inst.pwdata & 8'b10110011))
$display("READ WRITE COMPARISON TCR --> TRUE");
else
$display("READ WRITE COMPARISON TCR --> FALSE");
end else begin
if(prdata == 8'h00)
$display("READ WRITE COMPARISON TSR --> TRUE");
else
$display("READ WRITE COMPARISON TSR --> FALSE");
end
end
end
`endif
// TESTCASE 6
`ifdef countup_forkjoin_pclk2
// write a random value to TDR
apb_write(8'h00, 8'd78);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// SET THE CONDITION FOR OPERATION
// LOAD --> 0, COUNTUP, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00110000);
fork
begin
$display("THREAD 1 %0t", $time);
repeat(257 - tdr_reg) begin
@(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("time %0t", $time);
end
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Pass %0t", $time);
else
$display("Fail %0t", $time);
end
begin
$display("THREAD 2 %0t", $time);
repeat((257 - tdr_reg) * 2/3) @(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Fail --> NOT NORMAL %0t", $time);
else
$display("Pass --> NORMAL OPERATION %0t", $time);
end
join
`endif
// TESTCASE 7
`ifdef countup_forkjoin_pclk4
// write a random value to TDR
apb_write(8'h00, 8'd78);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000001);
// SET THE CONDITION FOR OPERATION
// LOAD --> 0, COUNTUP, PCLKx4, EN --> 1
apb_write(8'h01, 8'b00110001);
fork
begin
$display("THREAD 1 %0t", $time);
repeat(257 - tdr_reg) begin
@(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("time %0t", $time);
end
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Pass %0t", $time);
else
$display("Fail %0t", $time);
end
begin
$display("THREAD 2 %0t", $time);
repeat((257 - tdr_reg) * 2/3) @(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Fail --> NOT NORMAL %0t", $time);
else
$display("Pass --> NORMAL OPERATION %0t", $time);
end
join
`endif
// TESTCASE 8
`ifdef countup_forkjoin_pclk8
// write a random value to TDR
apb_write(8'h00, 8'd78);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000010);
// SET THE CONDITION FOR OPERATION
// LOAD --> 0, COUNTUP, PCLKx8, EN --> 1
apb_write(8'h01, 8'b00110010);
fork
begin
$display("THREAD 1 %0t", $time);
repeat(257 - tdr_reg) begin
@(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("time %0t", $time);
end
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Pass %0t", $time);
else
$display("Fail %0t", $time);
end
begin
$display("THREAD 2 %0t", $time);
repeat((257 - tdr_reg) * 2/3) @(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Fail --> NOT NORMAL %0t", $time);
else
$display("Pass --> NORMAL OPERATION %0t", $time);
end
join
`endif
// TESTCASE 9
`ifdef countup_forkjoin_pclk16
// write a random value to TDR
apb_write(8'h00, 8'd78);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000011);
// SET THE CONDITION FOR OPERATION
// LOAD --> 0, COUNTUP, PCLKx8, EN --> 1
apb_write(8'h01, 8'b00110011);
fork
begin
$display("THREAD 1 %0t", $time);
repeat(257 - tdr_reg) begin
@(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("time %0t", $time);
end
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Pass %0t", $time);
else
$display("Fail %0t", $time);
end
begin
$display("THREAD 2 %0t", $time);
repeat((257 - tdr_reg) * 2/3) @(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Fail --> NOT NORMAL %0t", $time);
else
$display("Pass --> NORMAL OPERATION %0t", $time);
end
join
`endif
// TESTCASE 14
`ifdef countup_pause_countup_pclk2
// write a random value to TDR
apb_write(8'h00, 8'd200);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTUP, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00110000);
// count up for 250 ns
#800;
@(posedge pclk);
apb_write(8'h01, 8'b00100000);
#800;
if(s_ovf)
$display("OVERFLOW TRIGGERED -->> FAILED");
else
$display("OVERFLOW NOT TRIGGERED -->> NORMAL OPERATION %0t", $time);
$display("CONTINUE COUNTING %0t", $time);
// @(posedge pclk);
apb_write(8'h01, 8'b00110000);
wait(cnt == 8'h00);
repeat(2) @(posedge pclk);
if(s_ovf)
$display("PASS %0t", $time);
else
$display("FAULTY %0t", $time);
`endif
// TESTCASE 15
`ifdef countdw_pause_countdw_pclk2
// write a random value to TDR
apb_write(8'h00, 8'd50);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTDOWN, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00010000);
// count down for 250 ns
#800;
// EN --> 0 --> stop counting
@(posedge pclk);
apb_write(8'h01, 8'b00000000);
// wait for 250 ns
#800;
// CHECK TO SEE OPERATION IS NORMAL
if(s_udf)
$display("UNDERFLOW TRIGGERED -->> FAILED");
else
$display("UNDERFLOW NOT TRIGGERED -->> NORMAL OPERATION %0t", $time);
// SET EN --> 1 --> CONTINUE COUNTING
$display("CONTINUE COUNTING %0t", $time);
apb_write(8'h01, 8'b00010000);
wait(cnt == 8'hff);
repeat(2) @(posedge pclk);
// IF S_UDF FLAG TRIGGERED, PASS
if(s_udf)
$display("TESTCASE 15 --> PASS %0t", $time);
else
$display("TESTCASE 15 --> FAULTY %0t", $time);
`endif
// TESTCASE 16
`ifdef countup_reset_countdw_pclk2
// write a random value to TDR
apb_write(8'h00, 8'b10011000);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTUP, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00110000);
// COUNT UP FOR 250 ns
#250;
// RESET SIGNAL TRIGGERED
presetn = 0;
#100;
$display("TDR %0d, TCR %0d, TSR %0d %0t", tdr_reg, tcr_reg, tsr_reg, $time);
// CHECK IF TDR, TCR, TSR EQUAL DEFAULT VALUES (0)
if(tdr_reg == 8'h00 & tcr_reg == 8'h00 & tsr_reg == 8'h00)
$display("TDR, TCR, TSR EQUAL DEFAULT VALUES (0) --> NORMAL OPERATION %0t", $time);
else
$display("TDR, TCR, TSR NOT EQUAL DEFAULT VALUES (0) --> FAILED %0t", $time);
// RESET SIGNAL TRIGGERED
presetn = 1;
// PUT VALUE SIMILAR TO THE ONE BEFORE THE RESET INTO TCNT
// @(posedge pclk);
apb_write(8'h00, 8'ha4);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTDOWN, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00010000);
wait(cnt == 8'hff);
repeat(2) @(posedge pclk);
//CHECK IF S_UDF FLAG TRIGGERED --> PASS, OTHERWISE --> FAIL
if(s_udf)
$display("TESTCASE 16 --> PASS %0t", $time);
else
$display("TESTCASE 16 --> FAULTY %0t", $time);
`endif
// TESTCASE 17
`ifdef countdw_reset_countup_pclk2
// write a random value to TDR
apb_write(8'h00, 8'b10011000);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTDOWN, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00010000);
// COUNT DOWN FOR 250 ns
#250;
// RESET SIGNAL TRIGGERED
presetn = 0;
#100;
// CHECK IF TDR, TCR, TSR EQUAL DEFAULT VALUES (0)
if(tdr_reg == 8'h00 & tcr_reg == 8'h00 & tsr_reg == 8'h00)
$display("TDR, TCR, TSR EQUAL DEFAULT VALUES (0) --> NORMAL OPERATION %0t", $time);
else
$display("TDR, TCR, TSR NOT EQUAL DEFAULT VALUES (0) --> FAILED %0t", $time);
// RESET SIGNAL TRIGGERED
presetn = 1;
// PUT VALUE SIMILAR TO THE ONE BEFORE THE RESET INTO TCNT
@(posedge pclk);
apb_write(8'h00, 8'h8c);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTUP, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00110000);
wait(cnt == 8'h00);
repeat(2) @(posedge pclk);
//CHECK IF S_OVF FLAG TRIGGERED --> PASS, OTHERWISE --> FAIL
if(s_ovf)
$display("TESTCASE 17 --> PASS %0t", $time);
else
$display("TESTCASE 17 --> FAIL %0t", $time);
`endif
// TESTCASE 18
`ifdef countup_reset_load_countdw_pclk2
// write a random value to TDR
apb_write(8'h00, 8'b10011000);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTUP, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00110000);
// COUNT UP FOR 250 ns
#250;
// RESET SIGNAL TRIGGERED
presetn = 0;
#100;
// CHECK IF TDR, TCR, TSR EQUAL DEFAULT VALUES (0)
if(tdr_reg == 8'h00 & tcr_reg == 8'h00 & tsr_reg == 8'h00)
$display("TDR, TCR, TSR EQUAL DEFAULT VALUES (0) --> NORMAL OPERATION %0t", $time);
else
$display("TDR, TCR, TSR NOT EQUAL DEFAULT VALUES (0) --> FAILED %0t", $time);
// RESET SIGNAL TRIGGERED
presetn = 1;
// PUT A RANDOM VALUE INTO TCNT
@(posedge pclk);
apb_write(8'h00, 8'hd3);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTDOWN, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00010000);
wait(cnt == 8'hff);
repeat(2) @(posedge pclk);
//CHECK IF S_UDF FLAG TRIGGERED --> PASS, OTHERWISE --> FAIL
if(s_udf)
$display("TESTCASE 18 --> PASS %0t", $time);
else
$display("TESTCASE 18 --> FAIL %0t", $time);
`endif
// TESTCASE 19
`ifdef countdw_reset_load_countdw_pclk2
// write a random value to TDR
apb_write(8'h00, 8'b10011000);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTDOWN, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00010000);
// COUNT DOWN FOR 250 ns
#250;
// RESET SIGNAL TRIGGERED
presetn = 0;
#100;
// CHECK IF TDR, TCR, TSR EQUAL DEFAULT VALUES (0)
if(tdr_reg == 8'h00 & tcr_reg == 8'h00 & tsr_reg == 8'h00)
$display("TDR, TCR, TSR EQUAL DEFAULT VALUES (0) --> NORMAL OPERATION %0t", $time);
else
$display("TDR, TCR, TSR NOT EQUAL DEFAULT VALUES (0) --> FAILED %0t", $time);
// RESET SIGNAL TRIGGERED
presetn = 1;
// PUT VALUE SIMILAR TO THE ONE BEFORE THE RESET INTO TCNT
@(posedge pclk);
apb_write(8'h00, 8'hd3);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// set the conditions for operation
// LOAD --> 0, COUNTDOWN, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00010000);
wait(cnt == 8'hff);
repeat(2) @(posedge pclk);
//CHECK IF S_UDF FLAG TRIGGERED --> PASS, OTHERWISE --> FAIL
if((cnt == 8'hff) & s_udf)
$display("TESTCASE 19 --> PASS %0t", $time);
else
$display("TESTCASE 19 --> FAIL %0t", $time);
`endif
// TESTCASE 20
`ifdef fake_underflow
// WRITE 8'H00 TO TDR
apb_write(8'h00, 8'h00);
// SET LOAD --> 1 TO LOAD DATA FROM TDR TO TCNT
apb_write(8'h01, 8'b10000000);
// SET THE CONDITION FOR OPERATION
// LOAD --> 0, COUNTDOWN, PCLKx2, EN --> 0
apb_write(8'h01, 8'b00000000);
// WRITE 8'HFF TO TDR
apb_write(8'h00, 8'hff);
// SET LOAD --> 1 TO LOAD DATA FROM TDR TO TCNT
apb_write(8'h01, 8'b10000000);
// SET LOAD --> 0
apb_write(8'h01, 8'b00000000);
`endif
// TESTCASE 21
`ifdef fake_overflow
// WRITE 8'HFF TO TDR
apb_write(8'h00, 8'hff);
// SET LOAD --> 1 TO LOAD DATA FROM TDR TO TCNT
apb_write(8'h01, 8'b10000000);
// SET THE CONDITION FOR OPERATION
// LOAD --> 0, COUNTUP, PCLKx2, EN --> 0
apb_write(8'h01, 8'b00100000);
// WRITE 8'H00 TO TDR
apb_write(8'h00, 8'h00);
// SET LOAD --> 1 TO LOAD DATA FROM TDR TO TCNT
apb_write(8'h01, 8'b10100000);
// SET LOAD --> 0
apb_write(8'h01, 8'b00100000);
`endif
#4000;
$finish;
end
initial begin
// Dump waves
$dumpfile("dump.vcd");
$dumpvars(1, tb_timer);
$dumpvars(1, dut);
$dumpvars(1, dut.register_control_inst);
$dumpvars(1, dut.logic_control_inst);
$dumpvars(1, dut.counter_inst);
end
endmodule
In the testbench above, I want to ask about the testcase 6. The description is that:
-Read a random number less than 255.
-Thread 1: Counts down with an internal clock of pclk × 2, starting from the number above. If the Thread 1 count-down goes below 0, it checks if the underflow status is triggered. If so, it displays a "pass" message (normal operation); otherwise, it displays a "faulty" message.
-Thread 2 runs counting-down in parallel with Thread 1, but Thread 2's job duration is shorter than Thread 1's (about 2/3 of Thread 1's duration).
-Thread 2 eventually stops its counting job (while Thread 1 is still running) and checks if an underflow occurred. If underflow occurs, it displays a "faulty" message; otherwise, it displays a "pass" (normal operation).
// TESTCASE 6
`ifdef countup_forkjoin_pclk2
// write a random value to TDR
apb_write(8'h00, 8'd78);
// SET LOAD --> 1 to load data from TDR to tcnt
apb_write(8'h01, 8'b10000000);
// SET[justify]Text[/justify] THE CONDITION FOR OPERATION
// LOAD --> 0, COUNTUP, PCLKx2, EN --> 1
apb_write(8'h01, 8'b00110000);
fork
begin
$display("THREAD 1 %0t", $time);
repeat(257 - tdr_reg) begin
@(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("time %0t", $time);
end
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Pass %0t", $time);
else
$display("Fail %0t", $time);
end
begin
$display("THREAD 2 %0t", $time);
repeat((257 - tdr_reg) * 2/3) @(posedge tb_timer.dut.logic_control_inst.clk_in);
$display("cnt is %0d and s_ovf is %0d", cnt, s_ovf);
if(s_ovf)
$display("Fail --> NOT NORMAL %0t", $time);
else
$display("Pass --> NORMAL OPERATION %0t", $time);
end
join
`endif
My question is: Is there a way to stop counting up in thread 2 of second block in the fork join while thread 1 is still running. If I try to stop it by turning off the ENABLE bit (bit 4) in TCR register (address 8’h01) by doing
apb_write(8’h01, 8’b00100000);
it will stop the counting of thread 1 also and thread 1 can not trigger overflow flag.