// Register all inputs always_ff @ (posedge clk, posedge rst) begin if (rst) begin op_in_r <= '0; a_in_r <= '0; b_in_r <= '0; in_valid_r <= '0; end else begin op_in_r <= op_in; a_in_r <= a_in; b_in_r <= b_in; in_valid_r <= in_valid; end end
// Compute the result always_comb begin result = '0; if (in_valid_r) begin case (op_in_r) add: result = a_in_r + b_in_r; sub: result = a_in_r + (~b_in_r+1'b1); default: result = '0; endcase end end
// Register outputs always_ff @ (posedge clk, posedge rst) begin if (rst) begin out <= '0; out_valid <= '0; end else begin out <= result; out_valid <= in_valid_r; end end
endmodule;
在使用 C++ 编写测试用例之前,我们需要首先将 sv 代码编译成 C++ 代码:
1
verilator --cc alu.sv
cc 参数告诉 Verilator 将源代码转化成 C++ 代码,在编译之后生成了一个 obj_dir 文件夹,其中 mk 文件用于使用 Make 构建仿真的可执行程序,.h 和 .cpp 文件包含我们源代码实现的信息。
virtualvoidreset(void){ m_core->i_reset = 1; // Make sure any inheritance gets applied this->tick(); m_core->i_reset = 0; }
virtualvoidtick(void){ // Increment our own internal time reference m_tickcount++;
// Make sure any combinatorial logic depending upon // inputs that may have changed before we called tick() // has settled before the rising edge of the clock. m_core->i_clk = 0; m_core->eval();
virtualvoidtick(void){ // Request that the testbench toggle the clock within // Verilator TESTBENCH<Vmodule>::tick();
bool writeout = false; // Check for debugging conditions // // For example: // // 1. We might be interested any time a wishbone master // command is accepted // if ((m_core->v__DOT__wb_stb)&&(!m_core->v__DOT__wb_stall)) writeout = true; // // 2. as well as when the slave finally responds // if (m_core->v__DOT__wb_ack) writeout = true;
if (writeout) { // Now we'll debug by printf's and examine the // internals of m_core printf("%8ld: %s %s ...\n", m_tickcount, (m_core->v__DOT__wb_cyc)?"CYC":" ", (m_core->v__DOT__wb_stb)?"STB":" ", ... ); } } }
template<classMODULE>classTESTBENCH { // Need to add a new class variable VerilatedVcdC *m_trace; ...
TESTBENCH(void) { // According to the Verilator spec, you *must* call // traceEverOn before calling any of the tracing functions // within Verilator. Verilated::traceEverOn(true); ... // Everything else can stay like it was before }
// Open/create a trace file virtualvoidopentrace(constchar *vcdname){ if (!m_trace) { m_trace = new VerilatedVcdC; m_core->trace(m_trace, 99); m_trace->open(vcdname); } }
// Close a trace file virtualvoidclose(void){ if (m_trace) { m_trace->close(); m_trace = NULL; } }
virtualvoidtick(void){ // Make sure the tickcount is greater than zero before // we do this m_tickcount++;
// Allow any combinatorial logic to settle before we tick // the clock. This becomes necessary in the case where // we may have modified or adjusted the inputs prior to // coming into here, since we need all combinatorial logic // to be settled before we call for a clock tick. // m_core->i_clk = 0; m_core->eval();
// // Here's the new item: // // Dump values to our trace file // if(trace) m_trace->dump(10*m_tickcount-2);
// Repeat for the positive edge of the clock m_core->i_clk = 1; m_core->eval(); if(trace) m_trace->dump(10*m_tickcount);
// Now the negative edge m_core->i_clk = 0; m_core->eval(); if (m_trace) { // This portion, though, is a touch different. // After dumping our values as they exist on the // negative clock edge ... m_trace->dump(10*m_tickcount+5); // // We'll also need to make sure we flush any I/O to // the trace file, so that we can use the assert() // function between now and the next tick if we want to. m_trace->flush(); } } }