598 lines
21 KiB
Plaintext
598 lines
21 KiB
Plaintext
package Testbench;
|
|
|
|
// ================================================================
|
|
// Testbench for basic sanity-check testing of the Debug Module.
|
|
|
|
|
|
// ================================================================
|
|
// BSV library imports
|
|
|
|
import FIFOF :: *;
|
|
import GetPut :: *;
|
|
import ClientServer :: *;
|
|
import Connectable :: *;
|
|
import StmtFSM :: *;
|
|
|
|
// ----------------
|
|
// Other library imports
|
|
|
|
import Semi_FIFOF :: *;
|
|
|
|
// ================================================================
|
|
|
|
import ISA_Decls :: *;
|
|
import TRX :: *;
|
|
|
|
import Debug_Module :: *;
|
|
|
|
// ================================================================
|
|
|
|
Integer csr_addr_dcsr = 'h7b0;
|
|
Integer csr_addr_dpc = 'h7b1;
|
|
Integer csr_addr_dscratch0 = 'h7b2;
|
|
Integer csr_addr_dscratch1 = 'h7b3;
|
|
|
|
// ================================================================
|
|
|
|
(* synthesize *)
|
|
module mkTestbench (Empty);
|
|
|
|
// ================================================================
|
|
// Cycle-counter and cycle-limit termination
|
|
|
|
Reg #(Bit #(32)) rg_cycle <- mkReg (0);
|
|
|
|
Integer cycle_limit = 100;
|
|
|
|
rule rl_count_cycles;
|
|
if (rg_cycle == fromInteger (100)) begin
|
|
$display ("Testench: stopping at cycle %0d", cycle_limit);
|
|
$finish (0);
|
|
end
|
|
rg_cycle <= rg_cycle +1;
|
|
endrule
|
|
|
|
// ================================================================
|
|
// The Debug Module
|
|
|
|
Debug_Module_IFC dm <- mkDebug_Module;
|
|
|
|
// ================================================================
|
|
// Model of a hart, and connections to dm
|
|
|
|
Hart_DM_IFC hart0 <- mkHart_Model (0);
|
|
|
|
// Reset
|
|
mkConnection (dm.hart0_reset_req, hart0.hart_reset_req);
|
|
|
|
// Run-control
|
|
mkConnection (dm.hart0_run_req_rsp, hart0.hart_run_req_rsp);
|
|
|
|
// GPR access
|
|
mkConnection (dm.master_for_gprs, hart0.slave_for_gprs);
|
|
|
|
// CSR access
|
|
mkConnection (dm.master_for_csrs, hart0.slave_for_csrs);
|
|
|
|
// ================================================================
|
|
// Over-simplified model of platform reset (all except DM)
|
|
|
|
rule rl_ndm_reset;
|
|
let x <- dm.ndm_reset_req.get;
|
|
$display ("Testbench.rl_ndm_reset: Resetting all platform except Debug Module");
|
|
endrule
|
|
|
|
// ================================================================
|
|
// Over-simplified model of system (RISC-V) memory
|
|
// On reads, return addr + 2.
|
|
|
|
rule rl_mem_read;
|
|
let rda <- pop_o (dm.master.fo_rda);
|
|
let data = rda.addr + 2; // Bogus data, for now
|
|
let rdr = TRX_RdR {trans_id: rda.trans_id,
|
|
status : TRX_OKAY,
|
|
data : data};
|
|
dm.master.fi_rdr.enq (rdr);
|
|
$display ("Testbench: memory read [0x%08h] => 0x%08h", rda.addr, data);
|
|
endrule
|
|
|
|
rule rl_mem_write;
|
|
let wra <- pop_o (dm.master.fo_wra);
|
|
let wrd <- pop_o (dm.master.fo_wrd);
|
|
let wrr = TRX_WrR {trans_id: wra.trans_id,
|
|
status : TRX_OKAY};
|
|
dm.master.fi_wrr.enq (wrr);
|
|
$display ("Testbench: memory write [0x%08h] <= 0x%08h", wra.addr, wrd.data);
|
|
endrule
|
|
|
|
// ================================================================
|
|
// Abstract command sequences (read/write GPR/CSR)
|
|
|
|
Reg #(Bit #(32)) rg_abstractcs <- mkRegU;
|
|
|
|
// Read a register
|
|
function Stmt fn_stmt_read_reg (Bit #(16) regno);
|
|
return
|
|
seq
|
|
$display ("----------------\nRead RISC-V reg");
|
|
// Clear any prior error status
|
|
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
|
|
// Perform the read
|
|
dm.write (dm_addr_command,
|
|
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
|
|
False, // postexec
|
|
True, // transfer
|
|
False, // write
|
|
regno));
|
|
// Read status to check no error
|
|
action
|
|
let x <- dm.av_read (dm_addr_abstractcs);
|
|
rg_abstractcs <= x;
|
|
endaction
|
|
while (fn_abstractcs_busy (rg_abstractcs)) seq
|
|
$display ("Testbench: read reg: busy");
|
|
action
|
|
let x <- dm.av_read (dm_addr_abstractcs);
|
|
rg_abstractcs <= x;
|
|
endaction
|
|
endseq
|
|
if (fn_abstractcs_cmderr (rg_abstractcs) != DM_ABSTRACTCS_CMDERR_NONE)
|
|
$display ("Testbench: read reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
|
|
else action
|
|
let x <- dm.av_read (dm_addr_data0);
|
|
$display ("Testbench: read reg => 0x%08h", x);
|
|
endaction
|
|
endseq;
|
|
endfunction
|
|
|
|
// Write a register
|
|
function Stmt fn_stmt_write_reg (Bit #(16) regno, Bit #(32) data);
|
|
return
|
|
seq
|
|
$display ("----------------\nWrite RISC-V reg");
|
|
// Clear any prior error status
|
|
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
|
|
// Write data0
|
|
dm.write (dm_addr_data0, data);
|
|
// Perform the write
|
|
dm.write (dm_addr_command,
|
|
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
|
|
False, // postexec
|
|
True, // transfer
|
|
True, // write
|
|
regno));
|
|
// Read status to check no error
|
|
action
|
|
let x <- dm.av_read (dm_addr_abstractcs);
|
|
rg_abstractcs <= x;
|
|
endaction
|
|
while (fn_abstractcs_busy (rg_abstractcs)) seq
|
|
$display ("Testbench: write reg: busy");
|
|
action
|
|
let x <- dm.av_read (dm_addr_abstractcs);
|
|
rg_abstractcs <= x;
|
|
endaction
|
|
endseq
|
|
$display ("Testbench: write reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
|
|
endseq;
|
|
endfunction
|
|
|
|
// ================================================================
|
|
// System Bus access sequences (read/write RISC-V memory)
|
|
|
|
Reg #(Bool) rg_busy <- mkRegU;
|
|
|
|
Reg #(Bit #(32)) rg_j <- mkRegU;
|
|
Reg #(Bit #(32)) rg_addr <- mkRegU;
|
|
Reg #(Bit #(32)) rg_data <- mkRegU;
|
|
|
|
Stmt stmt_wait_for_sb_nonbusy = (
|
|
seq
|
|
rg_busy <= True;
|
|
while (rg_busy) seq
|
|
delay (1);
|
|
action
|
|
let x <- dm.av_read (dm_addr_sbcs);
|
|
let sberror = fn_sbcs_sberror (x);
|
|
rg_busy <= (sberror == DM_SBERROR_BUSY_STALE);
|
|
if ( (sberror != DM_SBERROR_NONE)
|
|
&& (sberror != DM_SBERROR_BUSY_STALE))
|
|
begin
|
|
$display ("Testbench: stmt_wait_for_sb_nonbusy: ", fshow (sberror));
|
|
$finish (1);
|
|
end
|
|
endaction
|
|
endseq
|
|
endseq);
|
|
|
|
// Do a single-read from memory
|
|
Stmt stmt_mem_read_1 = (
|
|
seq
|
|
dm.write (dm_addr_sbaddress0, 'h1_0000);
|
|
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
|
|
DM_SBACCESS_32_BIT,
|
|
False, // sbautoincrement
|
|
False, // sbautoread
|
|
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
|
stmt_wait_for_sb_nonbusy;
|
|
action
|
|
let x <- dm.av_read (dm_addr_sbdata0);
|
|
$display ("stmt_mem_read_1: read-data = 0x%08h", x);
|
|
endaction
|
|
endseq);
|
|
|
|
// Do a multiple-read from memory
|
|
Stmt stmt_mem_read_4 = (
|
|
seq
|
|
dm.write (dm_addr_sbaddress0, 'h1_0000);
|
|
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
|
|
DM_SBACCESS_32_BIT,
|
|
True, // sbautoincrement
|
|
True, // sbautoread
|
|
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
|
for (rg_j <= 0; rg_j < 3; rg_j <= rg_j + 1) seq
|
|
stmt_wait_for_sb_nonbusy;
|
|
action
|
|
let x <- dm.av_read (dm_addr_sbdata0);
|
|
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
|
|
endaction
|
|
endseq
|
|
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
|
|
DM_SBACCESS_32_BIT,
|
|
False, // sbautoincrement
|
|
False, // sbautoread
|
|
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
|
stmt_wait_for_sb_nonbusy;
|
|
action
|
|
let x <- dm.av_read (dm_addr_sbdata0);
|
|
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
|
|
endaction
|
|
endseq);
|
|
|
|
// Do a single-write to memory
|
|
Stmt stmt_mem_write_1 = (
|
|
seq
|
|
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
|
|
DM_SBACCESS_32_BIT,
|
|
False, // sbautoincrement
|
|
False, // sbautoread
|
|
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
|
stmt_wait_for_sb_nonbusy;
|
|
dm.write (dm_addr_sbaddress0, 'h1_0000);
|
|
dm.write (dm_addr_sbdata0, 'h_BEEF);
|
|
endseq);
|
|
|
|
// Do a multiple-write to memory
|
|
Stmt stmt_mem_write_4 = (
|
|
seq
|
|
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
|
|
DM_SBACCESS_32_BIT,
|
|
True, // sbautoincrement
|
|
False, // sbautoread
|
|
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
|
stmt_wait_for_sb_nonbusy;
|
|
action
|
|
rg_addr <= 'h_2000;
|
|
rg_data <= 'h_DAFA_0000;
|
|
endaction
|
|
dm.write (dm_addr_sbaddress0, rg_addr);
|
|
for (rg_j <= 0; rg_j < 4; rg_j <= rg_j + 1) seq
|
|
stmt_wait_for_sb_nonbusy;
|
|
action
|
|
$display ("stmt_mem_write_4: [0x%08h] x = 0x%08h", rg_addr + rg_j, rg_data);
|
|
dm.write (dm_addr_sbdata0, rg_data);
|
|
rg_data <= rg_data + 1;
|
|
endaction
|
|
endseq
|
|
endseq);
|
|
|
|
// ================================================================
|
|
// Run-control test sequences (reset, run, halt, single-step)
|
|
|
|
let dmcontrol_dm_reset
|
|
= fn_mk_dmcontrol (False, // haltreq
|
|
False, // resumereq
|
|
False, // hartreset
|
|
False, // hasel
|
|
0, // hartsel,
|
|
False, // ndmreset
|
|
False); // dmactive; assert reset
|
|
|
|
let dmcontrol_ndmreset
|
|
= fn_mk_dmcontrol (False, // haltreq
|
|
False, // resumereq
|
|
False, // hartreset
|
|
False, // hasel,
|
|
0, // hartsel
|
|
True, // ndmreset
|
|
True); // dmactive
|
|
|
|
let dmcontrol_err_hasel
|
|
= fn_mk_dmcontrol (False, // haltreq
|
|
False, // resumereq
|
|
False, // hartreset
|
|
True, // hasel,
|
|
0, // hartsel
|
|
False, // ndmreset
|
|
True); // dmactive
|
|
|
|
let dmcontrol_err_hartsel
|
|
= fn_mk_dmcontrol (False, // haltreq
|
|
False, // resumereq
|
|
False, // hartreset
|
|
False, // hasel,
|
|
3, // hartsel
|
|
False, // ndmreset
|
|
True); // dmactive
|
|
|
|
let dmcontrol_hartreset
|
|
= fn_mk_dmcontrol (False, // haltreq
|
|
False, // resumereq
|
|
True, // hartreset
|
|
False, // hasel,
|
|
0, // hartsel
|
|
False, // ndmreset
|
|
True); // dmactive
|
|
|
|
let dmcontrol_err_haltreq_resumereq
|
|
= fn_mk_dmcontrol (True, // haltreq
|
|
True, // resumereq
|
|
False, // hartreset
|
|
False, // hasel,
|
|
0, // hartsel
|
|
False, // ndmreset
|
|
True); // dmactive
|
|
|
|
let dmcontrol_haltreq
|
|
= fn_mk_dmcontrol (True, // haltreq
|
|
False, // resumereq
|
|
False, // hartreset
|
|
False, // hasel,
|
|
0, // hartsel
|
|
False, // ndmreset
|
|
True); // dmactive
|
|
|
|
let dmcontrol_resumereq
|
|
= fn_mk_dmcontrol (False, // haltreq
|
|
True, // resumereq
|
|
False, // hartreset
|
|
False, // hasel,
|
|
0, // hartsel
|
|
False, // ndmreset
|
|
True); // dmactive
|
|
|
|
function Stmt fn_stmt_run_control (DM_Word dm_word);
|
|
return seq
|
|
dm.write (dm_addr_dmcontrol, dm_word);
|
|
delay (5);
|
|
// Check and show status
|
|
action
|
|
let x <- dm.av_read (dm_addr_dmstatus);
|
|
$display (" ", fshow_dmstatus (x));
|
|
endaction
|
|
endseq;
|
|
endfunction
|
|
|
|
// ----------------
|
|
// For single-step, set 'step' bit in DCSR, then run
|
|
|
|
let dcsr_step = {4'h4, // xdebugver
|
|
12'b0,
|
|
1'b0, // ebreakm
|
|
1'b0,
|
|
1'b0, // ebreaks
|
|
1'b0, // ebreaku
|
|
1'b0, // stepie
|
|
1'b0, // stepcount
|
|
1'b0, // steptime
|
|
3'b0, // cause
|
|
3'b0,
|
|
1'b1, // step
|
|
2'h3};
|
|
|
|
Stmt stmt_single_step = (
|
|
seq
|
|
// set 'step' in dcsr
|
|
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + csr_addr_dcsr),
|
|
dcsr_step); // priv
|
|
fn_stmt_run_control (dmcontrol_resumereq);
|
|
endseq);
|
|
|
|
// ================================================================
|
|
// Top-level test. Comment/Uncomment desired parts.
|
|
|
|
Stmt test = seq
|
|
// Reset DM
|
|
$display ("----------------\n'Testbench: Reset DM'");
|
|
fn_stmt_run_control (dmcontrol_dm_reset);
|
|
|
|
/*
|
|
$display ("----------------\n'Testbench: Reset Platform'");
|
|
fn_stmt_run_control (dmcontrol_ndmreset);
|
|
|
|
$display ("----------------\n'Testbench: Err hasel'");
|
|
fn_stmt_run_control (dmcontrol_err_hasel);
|
|
|
|
$display ("----------------\n'Testbench: Err hartsel'");
|
|
fn_stmt_run_control (dmcontrol_err_hartsel);
|
|
|
|
$display ("----------------\n'Testbench: Reset hart'");
|
|
fn_stmt_run_control (dmcontrol_hartreset);
|
|
|
|
$display ("----------------\n'Testbench: Err haltreq and resumereq'");
|
|
fn_stmt_run_control (dmcontrol_err_haltreq_resumereq);
|
|
|
|
$display ("----------------\n'Testbench: Continue'");
|
|
fn_stmt_run_control (dmcontrol_resumereq);
|
|
|
|
$display ("----------------\n'Testbench: Halt'");
|
|
fn_stmt_run_control (dmcontrol_haltreq);
|
|
|
|
$display ("----------------\n'Testbench: Single step'");
|
|
stmt_single_step;
|
|
*/
|
|
|
|
$display ("----------------\n'Testbench: Read GPR'");
|
|
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5));
|
|
$display ("----------------\n'Testbench: Read CSR'");
|
|
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3));
|
|
|
|
$display ("----------------\n'Testbench: Write GPR'");
|
|
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5), 'h_AAAA_0005);
|
|
$display ("----------------\n'Testbench: Write CSR'");
|
|
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3), 'h_CCCC_0003);
|
|
|
|
/*
|
|
$display ("----------------\n'Testbench: Read 1'");
|
|
stmt_mem_read_1;
|
|
|
|
$display ("----------------\n'Testbench: Write 1'");
|
|
stmt_mem_write_1;
|
|
|
|
$display ("----------------\n'Testbench: Read 4'");
|
|
stmt_mem_read_4;
|
|
|
|
$display ("----------------\n'Testbench: Write 4'");
|
|
stmt_mem_write_4;
|
|
*/
|
|
|
|
await (False);
|
|
endseq;
|
|
|
|
mkAutoFSM (test);
|
|
|
|
endmodule
|
|
|
|
// ================================================================
|
|
// Over-simplified model of a hart (reset, run/halt, read/write GPR/CSR)
|
|
|
|
interface Hart_DM_IFC;
|
|
// Reset
|
|
interface Put #(Token) hart_reset_req;
|
|
|
|
// Run-control
|
|
interface Server #(Bool, Bool) hart_run_req_rsp;
|
|
|
|
// GPR access
|
|
interface TRX_Slave_IFC #(5,32,0) slave_for_gprs;
|
|
|
|
// CSR access
|
|
interface TRX_Slave_IFC #(12,32,0) slave_for_csrs;
|
|
endinterface
|
|
|
|
|
|
(* synthesize *)
|
|
module mkHart_Model #(parameter Bit #(10) hart_id) (Hart_DM_IFC);
|
|
|
|
Reg #(Bool) rg_hart_running <- mkReg (False);
|
|
|
|
FIFOF #(Token) f_hart_reset_reqs <- mkFIFOF;
|
|
|
|
FIFOF #(Bool) f_hart_run_reqs <- mkFIFOF;
|
|
FIFOF #(Bool) f_hart_run_rsps <- mkFIFOF;
|
|
|
|
// TRX interface to gprs
|
|
TRX_Buffer_IFC #(5,32,0) trx_buf_gprs <- mkTRX_Buffer;
|
|
|
|
// TRX interface to crs
|
|
TRX_Buffer_IFC #(12,32,0) trx_buf_csrs <- mkTRX_Buffer;
|
|
|
|
// ----------------------------------------------------------------
|
|
// BEHAVIOR
|
|
|
|
// ----------------
|
|
// Reset
|
|
|
|
rule rl_hart_reset;
|
|
let x = f_hart_reset_reqs.first;
|
|
f_hart_reset_reqs.deq;
|
|
|
|
$display ("Testbench.hart [%0d]: reset", hart_id);
|
|
endrule
|
|
|
|
// ----------------
|
|
// Run-control
|
|
|
|
rule rl_resume_hart (f_hart_run_reqs.first);
|
|
f_hart_run_reqs.deq;
|
|
rg_hart_running <= True;
|
|
|
|
if (rg_hart_running)
|
|
$display ("Testbench.hart [%0d].rl_resume_hart: already running", hart_id);
|
|
else
|
|
$display ("Testbench.hart [%0d].rl_resume_hart: resuming", hart_id);
|
|
|
|
f_hart_run_rsps.enq (True);
|
|
endrule
|
|
|
|
rule rl_halt_hart (! f_hart_run_reqs.first);
|
|
f_hart_run_reqs.deq;
|
|
rg_hart_running <= False;
|
|
|
|
if (rg_hart_running)
|
|
$display ("Testbench.hart [%0d].rl_halt_hart: halting", hart_id);
|
|
else
|
|
$display ("Testbench.hart [%0d].rl_halt_hart: already halted", hart_id);
|
|
|
|
f_hart_run_rsps.enq (False);
|
|
endrule
|
|
|
|
// ----------------
|
|
// GPR access
|
|
|
|
rule rl_read_gpr;
|
|
let rda <- pop_o (trx_buf_gprs.master.fo_rda);
|
|
Bit #(32) data = extend (rda.addr) + 'h1000;
|
|
let rdr = TRX_RdR {trans_id: rda.trans_id,
|
|
status: TRX_OKAY,
|
|
data: data};
|
|
trx_buf_gprs.master.fi_rdr.enq (rdr);
|
|
$display ("Testbench.hart [%0d]: Read GPR [%0h] => 0x%08h", hart_id, rda.addr, data);
|
|
endrule
|
|
|
|
rule rl_read_csr;
|
|
let rda <- pop_o (trx_buf_csrs.master.fo_rda);
|
|
Bit #(32) data = extend (rda.addr) + 'h2000;
|
|
let rdr = TRX_RdR {trans_id: rda.trans_id,
|
|
status: TRX_OKAY,
|
|
data: data};
|
|
trx_buf_csrs.master.fi_rdr.enq (rdr);
|
|
$display ("Testbench.hart [%0d]: Read CSR [%0h] => 0x%08h", hart_id, rda.addr, data);
|
|
endrule
|
|
|
|
rule rl_write_gpr;
|
|
let wra <- pop_o (trx_buf_gprs.master.fo_wra);
|
|
let wrd <- pop_o (trx_buf_gprs.master.fo_wrd);
|
|
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
|
|
trx_buf_gprs.master.fi_wrr.enq (wrr);
|
|
$display ("Testbench.hart [%0d]: Write GPR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
|
|
endrule
|
|
|
|
rule rl_write_csr;
|
|
let wra <- pop_o (trx_buf_csrs.master.fo_wra);
|
|
let wrd <- pop_o (trx_buf_csrs.master.fo_wrd);
|
|
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
|
|
trx_buf_csrs.master.fi_wrr.enq (wrr);
|
|
$display ("Testbench.hart [%0d]: Write CSR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
|
|
endrule
|
|
|
|
// ----------------------------------------------------------------
|
|
// INTERFACE
|
|
|
|
// Reset
|
|
interface Put hart_reset_req = toPut (f_hart_reset_reqs);
|
|
|
|
// Run-control
|
|
interface Server hart_run_req_rsp = toGPServer (f_hart_run_reqs, f_hart_run_rsps);
|
|
|
|
// GPR access
|
|
interface TRX_Slave_IFC slave_for_gprs = trx_buf_gprs.slave;
|
|
|
|
// CSR access
|
|
interface TRX_Slave_IFC slave_for_csrs = trx_buf_csrs.slave;
|
|
endmodule
|
|
|
|
// ================================================================
|
|
|
|
endpackage
|