Files
Toooba/src_Core/Debug_Module/Test/Testbench.bsv
2019-03-26 14:49:40 -04:00

598 lines
18 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