From 41c0d44e5f2a5461d9b520aa9794d26c47bc677f Mon Sep 17 00:00:00 2001 From: Alfred Persson Forsberg Date: Mon, 27 Jan 2025 17:09:16 +0100 Subject: [PATCH] boilerplate for external interrupts Signed-off-by: Alfred Persson Forsberg --- bench/decoder_sim.v | 110 +++++++++++++++++++ bench/decoder_tb.cpp | 157 +++++++++++++++++++++++++++ bench/servant_external_sim.v | 42 +++++++ bench/servant_external_tb.cpp | 126 +++++++++++++++++++++ bench/servant_sim.v | 2 +- bench/servant_timer_sim.v | 43 ++++++++ bench/servant_timer_tb.cpp | 92 ++++++++++++++++ data/nexys_4.xdc | 17 +++ data/nexys_a7.xdc | 25 ++++- rtl/serv_csr.v | 36 ++++-- rtl/serv_debug.v | 4 +- rtl/serv_decode.v | 15 ++- rtl/serv_rf_top.v | 17 +++ rtl/serv_sleep.v | 20 ++++ rtl/serv_synth_wrapper.v | 3 +- rtl/serv_top.v | 31 +++++- serv.core | 34 ++++++ servant.core | 141 +++++++++++++++++++++--- servant/servant.v | 30 ++++- servant/servant_nexys_4.v | 53 +++++++++ servant/servant_nexys_4_clock_gen.v | 54 +++++++++ servant/servant_nexys_a7.v | 42 +++++++ servant/servant_nexys_a7_clock_gen.v | 57 ++++++++++ servant/servant_sleep_dummy.v | 47 ++++++++ servile/servile.v | 25 ++++- sw/Makefile | 6 +- sw/external_interrupt.S | 18 +++ sw/external_interrupt.hex | 16 +++ sw/external_interrupt_wfi.S | 17 +++ sw/external_interrupt_wfi.hex | 16 +++ sw/timer_interrupt.S | 22 ++++ sw/timer_interrupt.hex | 17 +++ sw/timer_interrupt_wfi.hex | 17 +++ sw/timer_interrupt_wfi_recurring.S | 29 +++++ 34 files changed, 1337 insertions(+), 44 deletions(-) create mode 100644 bench/decoder_sim.v create mode 100644 bench/decoder_tb.cpp create mode 100644 bench/servant_external_sim.v create mode 100644 bench/servant_external_tb.cpp create mode 100644 bench/servant_timer_sim.v create mode 100644 bench/servant_timer_tb.cpp create mode 100644 data/nexys_4.xdc create mode 100644 rtl/serv_sleep.v create mode 100644 servant/servant_nexys_4.v create mode 100644 servant/servant_nexys_4_clock_gen.v create mode 100644 servant/servant_nexys_a7.v create mode 100644 servant/servant_nexys_a7_clock_gen.v create mode 100644 servant/servant_sleep_dummy.v create mode 100644 sw/external_interrupt.S create mode 100644 sw/external_interrupt.hex create mode 100644 sw/external_interrupt_wfi.S create mode 100644 sw/external_interrupt_wfi.hex create mode 100644 sw/timer_interrupt.S create mode 100644 sw/timer_interrupt.hex create mode 100644 sw/timer_interrupt_wfi.hex create mode 100644 sw/timer_interrupt_wfi_recurring.S diff --git a/bench/decoder_sim.v b/bench/decoder_sim.v new file mode 100644 index 0000000..c273cbb --- /dev/null +++ b/bench/decoder_sim.v @@ -0,0 +1,110 @@ +`default_nettype none +module decoder_sim + (input wire wb_clk, + input wire [31:2] wb_rdt, + input wire wb_en, + output wire ebreak, + output wire jal_or_jalr, + output wire mret, + output wire wfi, + output wire sh_right, + output wire bne_or_bge, + output wire cond_branch, + output wire e_op, + output wire branch_op, + output wire shift_op, + output wire slt_or_branch, + output wire rd_op, + output wire two_stage_op, + output wire dbus_en, + output wire mdu_op, + output wire [2:0] ext_funct3, + output wire bufreg_rs1_en, + output wire bufreg_imm_en, + output wire bufreg_clr_lsb, + output wire bufreg_sh_signed, + output wire ctrl_utype, + output wire ctrl_pc_rel, + output wire alu_sub, + output wire [1:0] alu_bool_op, + output wire alu_cmp_eq, + output wire alu_cmp_sig, + output wire [2:0] alu_rd_sel, + output wire mem_signed, + output wire mem_word, + output wire mem_half, + output wire mem_cmd, + output wire csr_en, + output wire [1:0] csr_addr, + output wire csr_mstatus_en, + output wire csr_mie_en, + output wire csr_mcause_en, + output wire [1:0] csr_source, + output wire csr_d_sel, + output wire csr_imm_en, + output wire mtval_pc, + output wire [3:0] immdec_ctrl, + output wire [3:0] immdec_en, + output wire op_b_source, + output wire rd_mem_en, + output wire rd_csr_en, + output wire rd_alu_en +); + + // Instantiate the DUT + serv_decode + #(.PRE_REGISTER (1), + .MDU (0)) + dut ( + .clk(wb_clk), + .i_wb_rdt(wb_rdt), + .i_wb_en(wb_en), + // Outputs + .o_sh_right(sh_right), + .o_bne_or_bge(bne_or_bge), + .o_cond_branch(cond_branch), + .o_e_op(e_op), + .o_ebreak(ebreak), + .o_wfi(wfi), + .o_branch_op(branch_op), + .o_shift_op(shift_op), + .o_rd_op(rd_op), + .o_two_stage_op(two_stage_op), + .o_dbus_en(dbus_en), + .o_mdu_op(mdu_op), + .o_ext_funct3(ext_funct3), + .o_bufreg_rs1_en(bufreg_rs1_en), + .o_bufreg_imm_en(bufreg_imm_en), + .o_bufreg_clr_lsb(bufreg_clr_lsb), + .o_bufreg_sh_signed(bufreg_sh_signed), + .o_ctrl_jal_or_jalr(jal_or_jalr), + .o_ctrl_utype(ctrl_utype), + .o_ctrl_pc_rel(ctrl_pc_rel), + .o_ctrl_mret(mret), + .o_alu_sub(alu_sub), + .o_alu_bool_op(alu_bool_op), + .o_alu_cmp_eq(alu_cmp_eq), + .o_alu_cmp_sig(alu_cmp_sig), + .o_alu_rd_sel(alu_rd_sel), + .o_mem_signed(mem_signed), + .o_mem_word(mem_word), + .o_mem_half(mem_half), + .o_mem_cmd(mem_cmd), + .o_csr_en(csr_en), + .o_csr_addr(csr_addr), + .o_csr_mstatus_en(csr_mstatus_en), + .o_csr_mie_en(csr_mie_en), + .o_csr_mcause_en(csr_mcause_en), + .o_csr_source(csr_source), + .o_csr_d_sel(csr_d_sel), + .o_csr_imm_en(csr_imm_en), + .o_mtval_pc(mtval_pc), + .o_immdec_ctrl(immdec_ctrl), + .o_immdec_en(immdec_en), + .o_op_b_source(op_b_source), + .o_rd_mem_en(rd_mem_en), + .o_rd_csr_en(rd_csr_en), + .o_rd_alu_en(rd_alu_en) + ); + +endmodule diff --git a/bench/decoder_tb.cpp b/bench/decoder_tb.cpp new file mode 100644 index 0000000..6fc0d83 --- /dev/null +++ b/bench/decoder_tb.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verilated_vcd_c.h" +#include "Vdecoder_sim.h" + +void INThandler(int signal) { + std::cout << "\nCaught ctrl-c\n"; + std::exit(0); +} + +void test_instruction(Vdecoder_sim *top, const std::string& instr_name, uint32_t instr); +void cycle(Vdecoder_sim *top); + +int main(int argc, char** argv, char** env) { + Verilated::commandArgs(argc, argv); + + Vdecoder_sim *top = new Vdecoder_sim; + std::signal(SIGINT, INThandler); + + top->wb_clk = 0; + top->wb_rdt = 0; + top->wb_en = 0; + + std::cout << "Testing decoder\n"; + test_instruction(top, "jalr", 0x0000006F); + test_instruction(top, "mret", 0x30200073); + test_instruction(top, "ebreak", 0x00100073); + test_instruction(top, "wfi", 0x10500073); + test_instruction(top, "add", 0x00000033); + test_instruction(top, "addi", 0x00000013); + test_instruction(top, "sub", 0x40000033); + test_instruction(top, "and", 0x00007033); + test_instruction(top, "andi", 0x00007013); + test_instruction(top, "or", 0x00006033); + test_instruction(top, "ori", 0x00006013); + test_instruction(top, "xor", 0x00004033); + test_instruction(top, "xori", 0x00004013); + test_instruction(top, "sll", 0x00001033); + test_instruction(top, "slli", 0x00001013); + test_instruction(top, "srl", 0x00005033); + test_instruction(top, "srli", 0x00005013); + test_instruction(top, "sra", 0x40005033); + test_instruction(top, "srai", 0x40005013); + test_instruction(top, "lui", 0x00000037); + test_instruction(top, "auipc", 0x00000017); + test_instruction(top, "lw", 0x00002003); + test_instruction(top, "lh", 0x00001003); + test_instruction(top, "lhu", 0x00005003); + test_instruction(top, "lb", 0x00000003); + test_instruction(top, "lbu", 0x00004003); + test_instruction(top, "sw", 0x00002023); + test_instruction(top, "sh", 0x00001023); + test_instruction(top, "sb", 0x00000023); + test_instruction(top, "jal", 0x0000006F); + test_instruction(top, "jalr", 0x00000067); + test_instruction(top, "beq", 0x00000063); + test_instruction(top, "bne", 0x00001063); + test_instruction(top, "blt", 0x00004063); + test_instruction(top, "bge", 0x00005063); + test_instruction(top, "bltu", 0x00006063); + test_instruction(top, "bgeu", 0x00007063); + test_instruction(top, "slt", 0x00002033); + test_instruction(top, "slti", 0x00002013); + test_instruction(top, "sltu", 0x00003033); + test_instruction(top, "sltiu", 0x00003013); + test_instruction(top, "ecall", 0x00000073); + test_instruction(top, "ebreak", 0x00100073); + test_instruction(top, "fence", 0x0000000F); + test_instruction(top, "fence.i", 0x0000100F); + + test_instruction(top, "csrrw", 0x00001073); + test_instruction(top, "csrrs", 0x00002073); + test_instruction(top, "csrrc", 0x00003073); + test_instruction(top, "csrrwi", 0x00005073); + test_instruction(top, "csrrsi", 0x00006073); + test_instruction(top, "csrrci", 0x00007073); + return 0; +} + +void cycle(Vdecoder_sim *top) { + top->eval(); + top->wb_clk = 0; + top->eval(); + top->wb_clk = 1; + top->eval(); +} + +void test_instruction(Vdecoder_sim *top, const std::string& instr_name, uint32_t instr) { + std::ofstream outfile(instr_name + ".txt"); + + std::cout << "Testing " << instr_name << "\n"; + outfile << "Testing " << instr_name << "\n"; + + top->wb_rdt = instr >> 2; + top->wb_en = 1; + cycle(top); + + outfile << "jal_or_jalr: " << std::to_string(top->jal_or_jalr) << std::endl; + outfile << "ebreak: " << std::to_string(top->ebreak) << std::endl; + outfile << "mret: " << std::to_string(top->mret) << std::endl; + outfile << "wfi: " << std::to_string(top->wfi) << std::endl; + outfile << "sh_right: " << std::to_string(top->sh_right) << std::endl; + outfile << "bne_or_bge: " << std::to_string(top->bne_or_bge) << std::endl; + outfile << "cond_branch: " << std::to_string(top->cond_branch) << std::endl; + outfile << "e_op: " << std::to_string(top->e_op) << std::endl; + outfile << "branch_op: " << std::to_string(top->branch_op) << std::endl; + outfile << "shift_op: " << std::to_string(top->shift_op) << std::endl; + outfile << "slt_or_branch: " << std::to_string(top->slt_or_branch) << std::endl; + outfile << "rd_op: " << std::to_string(top->rd_op) << std::endl; + outfile << "two_stage_op: " << std::to_string(top->two_stage_op) << std::endl; + outfile << "dbus_en: " << std::to_string(top->dbus_en) << std::endl; + outfile << "mdu_op: " << std::to_string(top->mdu_op) << std::endl; + outfile << "ext_funct3: " << std::to_string(top->ext_funct3) << std::endl; + outfile << "bufreg_rs1_en: " << std::to_string(top->bufreg_rs1_en) << std::endl; + outfile << "bufreg_imm_en: " << std::to_string(top->bufreg_imm_en) << std::endl; + outfile << "bufreg_clr_lsb: " << std::to_string(top->bufreg_clr_lsb) << std::endl; + outfile << "bufreg_sh_signed: " << std::to_string(top->bufreg_sh_signed) << std::endl; + outfile << "ctrl_utype: " << std::to_string(top->ctrl_utype) << std::endl; + outfile << "ctrl_pc_rel: " << std::to_string(top->ctrl_pc_rel) << std::endl; + outfile << "alu_sub: " << std::to_string(top->alu_sub) << std::endl; + outfile << "alu_bool_op: " << std::to_string(top->alu_bool_op) << std::endl; + outfile << "alu_cmp_eq: " << std::to_string(top->alu_cmp_eq) << std::endl; + outfile << "alu_cmp_sig: " << std::to_string(top->alu_cmp_sig) << std::endl; + outfile << "alu_rd_sel: " << std::to_string(top->alu_rd_sel) << std::endl; + outfile << "mem_signed: " << std::to_string(top->mem_signed) << std::endl; + outfile << "mem_word: " << std::to_string(top->mem_word) << std::endl; + outfile << "mem_half: " << std::to_string(top->mem_half) << std::endl; + outfile << "mem_cmd: " << std::to_string(top->mem_cmd) << std::endl; + outfile << "csr_en: " << std::to_string(top->csr_en) << std::endl; + outfile << "csr_addr: " << std::to_string(top->csr_addr) << std::endl; + outfile << "csr_mstatus_en: " << std::to_string(top->csr_mstatus_en) << std::endl; + outfile << "csr_mie_en: " << std::to_string(top->csr_mie_en) << std::endl; + outfile << "csr_mcause_en: " << std::to_string(top->csr_mcause_en) << std::endl; + outfile << "csr_source: " << std::to_string(top->csr_source) << std::endl; + outfile << "csr_d_sel: " << std::to_string(top->csr_d_sel) << std::endl; + outfile << "csr_imm_en: " << std::to_string(top->csr_imm_en) << std::endl; + outfile << "mtval_pc: " << std::to_string(top->mtval_pc) << std::endl; + outfile << "immdec_ctrl: " << std::to_string(top->immdec_ctrl) << std::endl; + outfile << "immdec_en: " << std::to_string(top->immdec_en) << std::endl; + outfile << "op_b_source: " << std::to_string(top->op_b_source) << std::endl; + outfile << "rd_mem_en: " << std::to_string(top->rd_mem_en) << std::endl; + outfile << "rd_csr_en: " << std::to_string(top->rd_csr_en) << std::endl; + outfile << "rd_alu_en: " << std::to_string(top->rd_alu_en) << std::endl; + + assert(!(top->wfi && instr_name != "wfi")); + + top->wb_en = 0; + cycle(top); + outfile.close(); +} diff --git a/bench/servant_external_sim.v b/bench/servant_external_sim.v new file mode 100644 index 0000000..506882c --- /dev/null +++ b/bench/servant_external_sim.v @@ -0,0 +1,42 @@ +`default_nettype none +module servant_external_sim + (input wire wb_clk, + input wire wb_rst, + input wire ext_irq, + output wire [31:0] pc_adr, + output wire pc_vld, + output wire q, + output wire mret + ); + + parameter memfile = ""; + parameter memsize = 8192; + parameter width = 1; + parameter with_csr = 1; + parameter compressed = 0; + parameter align = compressed; + parameter interrupt_time = 3000; + + reg [1023:0] firmware_file; + initial + if ($value$plusargs("firmware=%s", firmware_file)) begin + $display("Loading RAM from %0s", firmware_file); + $readmemh(firmware_file, dut.servant.ram.mem); + end + + servant_sleep_dummy + #(.memfile (memfile), + .memsize (memsize), + .width (width), + .debug (1'b1), + .sim (1), + .with_csr (with_csr), + .compress (compressed[0:0]), + .align (align[0:0])) + dut(wb_clk, wb_rst, ext_irq, q); + + assign pc_adr = dut.servant.wb_mem_adr; + assign pc_vld = dut.servant.wb_mem_ack; + assign mret = dut.servant.cpu.cpu.mret; + +endmodule diff --git a/bench/servant_external_tb.cpp b/bench/servant_external_tb.cpp new file mode 100644 index 0000000..3aad21a --- /dev/null +++ b/bench/servant_external_tb.cpp @@ -0,0 +1,126 @@ +#include +#include +#include + +#include "verilated_vcd_c.h" +#include "Vservant_external_sim.h" + +#include + +using namespace std; + +static bool done; + +vluint64_t main_time = 0; // Current simulation time +// This is a 64-bit integer to reduce wrap over issues and +// allow modulus. You can also use a double, if you wish. + +double sc_time_stamp () { // Called by $time in Verilog + return main_time; // converts to double, to match + // what SystemC does +} + +void INThandler(int signal) { + printf("\nCaught ctrl-c\n"); + done = true; +} + +int reset(Vservant_external_sim *, VerilatedVcdC *); +int timer_test(Vservant_external_sim *, VerilatedVcdC *, + vluint64_t, vluint64_t); + +int main(int argc, char **argv, char **env) { + Verilated::commandArgs(argc, argv); + + Vservant_external_sim* top = new Vservant_external_sim; + + VerilatedVcdC * tfp = 0; + const char *vcd = Verilated::commandArgsPlusMatch("vcd="); + if (vcd[0]) { + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + top->trace (tfp, 99); + tfp->open ("trace.vcd"); + } + + signal(SIGINT, INThandler); + + vluint64_t timeout = 0; + const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout="); + if (arg_timeout[0]) + timeout = atoi(arg_timeout+9); + + vluint64_t interrupt_time = 1500; + const char *arg_interrupt_time = + Verilated::commandArgsPlusMatch("interrupt_time="); + if (arg_interrupt_time[0]) { + interrupt_time = atoi(arg_interrupt_time); + } + + vluint64_t vcd_start = 0; + const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start="); + if (arg_vcd_start[0]) + vcd_start = atoi(arg_vcd_start+11); + + int cur_cycle = 0; + int last_cycle = 0; + std::time_t last_time = std::time(nullptr); + + top->wb_clk = 1; + top->ext_irq = 0; + bool q = top->q; + int clock = 0; + reset(top, tfp); + timer_test(top, tfp, timeout, interrupt_time); + if (tfp) { + tfp->close(); + } + exit(0); +} + + +int reset(Vservant_external_sim *top, VerilatedVcdC *tfp) { + top->wb_rst = 1; + for (int i = 0; i < 10; i++) { + top->wb_clk = !top->wb_clk; + top->eval(); + } + top->wb_clk = !top->wb_clk; + top->wb_rst = 0; + return 0; +} + +int timer_test(Vservant_external_sim *top, VerilatedVcdC *tfp, + vluint64_t timeout, vluint64_t interrupt_time) { + int clock = 0; + while (Verilated::gotFinish()) { + top->eval(); + if (top->wb_clk) { + clock++; + } + if (tfp && top->wb_clk) { + tfp->dump(clock); + } + if (clock >= interrupt_time && clock <= interrupt_time + 100) { + if (top->ext_irq == 0) { + printf("interrupting %d\n", clock); + } + top->ext_irq = 1; + } else { + top->ext_irq = 0; + } + if (top->wb_clk && top->pc_vld && top->pc_adr) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + printf("%d | %d\n", clock, top->pc_adr); + if (top->mret) { + printf("mret detected, exiting\n"); + exit(0); + } + } + if (timeout && (clock >= timeout)) { + printf("Timeout: Exiting at time %d\n", clock); + exit(0); + } + top->wb_clk = !top->wb_clk; + } +} diff --git a/bench/servant_sim.v b/bench/servant_sim.v index 2317279..b2bd74c 100644 --- a/bench/servant_sim.v +++ b/bench/servant_sim.v @@ -29,7 +29,7 @@ module servant_sim .with_csr (with_csr), .compress (compressed[0:0]), .align (align[0:0])) - dut(wb_clk, wb_rst, q); + dut(wb_clk, wb_clk, wb_rst, q); assign pc_adr = dut.wb_mem_adr; assign pc_vld = dut.wb_mem_ack; diff --git a/bench/servant_timer_sim.v b/bench/servant_timer_sim.v new file mode 100644 index 0000000..5bbbcfc --- /dev/null +++ b/bench/servant_timer_sim.v @@ -0,0 +1,43 @@ +`default_nettype none +module servant_timer_sim + (input wire wb_clk, + input wire wb_rst, + input wire timer_clk, + output wire [31:0] pc_adr, + output wire pc_vld, + output wire q, + output wire timer_irq + ); + + parameter memfile = ""; + parameter memsize = 8192; + parameter width = 1; + parameter with_csr = 1; + parameter compressed = 0; + parameter align = compressed; + + wire sleep; + wire ext_irq; + + reg [1023:0] firmware_file; + initial + if ($value$plusargs("firmware=%s", firmware_file)) begin + $display("Loading RAM from %0s", firmware_file); + $readmemh(firmware_file, dut.servant.ram.mem); + end + + servant_sleep_dummy + #(.memfile (memfile), + .memsize (memsize), + .width (width), + .debug (1'b1), + .sim (1), + .with_csr (with_csr), + .compress (compressed[0:0]), + .align (align[0:0])) + dut(wb_clk, wb_rst, ext_irq, q); + + assign pc_adr = dut.servant.wb_mem_adr; + assign pc_vld = dut.servant.wb_mem_ack; + assign timer_irq = dut.servant.timer_irq; +endmodule diff --git a/bench/servant_timer_tb.cpp b/bench/servant_timer_tb.cpp new file mode 100644 index 0000000..8d746ba --- /dev/null +++ b/bench/servant_timer_tb.cpp @@ -0,0 +1,92 @@ +#include +#include +#include + +#include "verilated_vcd_c.h" +#include "Vservant_timer_sim.h" + +#include + +using namespace std; + +static bool done; + +vluint64_t main_time = 0; // Current simulation time +// This is a 64-bit integer to reduce wrap over issues and +// allow modulus. You can also use a double, if you wish. + +double sc_time_stamp () { // Called by $time in Verilog + return main_time; // converts to double, to match + // what SystemC does +} + +void INThandler(int signal) +{ + printf("\nCaught ctrl-c\n"); + done = true; +} + +int main(int argc, char **argv, char **env) +{ + int baud_rate = 0; + + Verilated::commandArgs(argc, argv); + + Vservant_timer_sim* top = new Vservant_timer_sim; + + VerilatedVcdC * tfp = 0; + const char *vcd = Verilated::commandArgsPlusMatch("vcd="); + if (vcd[0]) { + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + top->trace (tfp, 99); + tfp->open ("trace.vcd"); + } + + signal(SIGINT, INThandler); + + vluint64_t timeout = 0; + const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout="); + if (arg_timeout[0]) + timeout = atoi(arg_timeout+9); + + vluint64_t vcd_start = 0; + const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start="); + if (arg_vcd_start[0]) + vcd_start = atoi(arg_vcd_start+11); + + int cur_cycle = 0; + int last_cycle = 0; + std::time_t last_time = std::time(nullptr); + + top->wb_clk = 0; + top->timer_clk = 0; + bool q = top->q; + int clock = 0; + top->eval(); + while (!(done || Verilated::gotFinish())) { + top->eval(); + if (top->wb_clk) { + clock++; + } + top->wb_rst = main_time < 1000; + if (tfp) { + tfp->dump(main_time); + } + if (top->wb_clk && top->pc_vld && top->pc_adr) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + printf("%d | %d\n", clock, top->pc_adr); + } + if (timeout && (main_time >= timeout)) { + printf("Timeout: Exiting at time %lu\n", main_time); + done = true; + } + top->wb_clk = !top->wb_clk; + top->timer_clk = top->wb_clk; + main_time+=31.25; + } + if (tfp) { + tfp->close(); + } + exit(0); +} diff --git a/data/nexys_4.xdc b/data/nexys_4.xdc new file mode 100644 index 0000000..ad8d748 --- /dev/null +++ b/data/nexys_4.xdc @@ -0,0 +1,17 @@ +## 100 MHz Clock Signal (period 10) +set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { i_clk }]; +create_clock -add -name sys_clk_pin -period 10 -waveform {0 5} [get_ports { i_clk }]; + +## External interrupt (to BTNL) +set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports {ext_irq}]; + +## LEDs +set_property -dict { PACKAGE_PIN T8 IOSTANDARD LVCMOS33 } [get_ports { q }]; + +## UART +set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { o_uart_tx }]; + +set_property -dict { PACKAGE_PIN C12 IOSTANDARD LVCMOS33 } [get_ports { i_rst }]; # ACTIVE LOW !!! + +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 3.3 [current_design] diff --git a/data/nexys_a7.xdc b/data/nexys_a7.xdc index 3906727..37cf909 100644 --- a/data/nexys_a7.xdc +++ b/data/nexys_a7.xdc @@ -1,4 +1,23 @@ -set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports i_clk]; -set_property -dict {PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports q] +## 100 MHz Clock Signal (period 10) +set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { i_clk }]; +create_clock -add -name sys_clk_pin -period 10 -waveform {0 5} [get_ports { i_clk }]; -create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports i_clk]; +## External interrupt (to BTNL) +set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports {ext_irq}]; + +## LEDs (LED0) +set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS33 } [get_ports { q }]; + +## UART +set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { o_uart_tx }]; + +set_property -dict { PACKAGE_PIN C12 IOSTANDARD LVCMOS33 } [get_ports { i_rst }]; # ACTIVE LOW !!! + +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 3.3 [current_design] + +## DEBUG (JA1) +#set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports { debug_wb_clk }]; #JA1 +#set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { q }]; #JA2 +#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports { debug_sleep_req }]; #JA3 +#set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports { debug_wakeup_req }]; #JA4 diff --git a/rtl/serv_csr.v b/rtl/serv_csr.v index 547accf..6f49021 100644 --- a/rtl/serv_csr.v +++ b/rtl/serv_csr.v @@ -19,6 +19,7 @@ module serv_csr input wire i_cnt_done, input wire i_mem_op, input wire i_mtip, + input wire i_meip, input wire i_trap, output reg o_new_irq, //Control @@ -36,7 +37,9 @@ module serv_csr output wire [B:0] o_csr_in, input wire [B:0] i_csr_imm, input wire [B:0] i_rs1, - output wire [B:0] o_q); + output wire [B:0] o_q, + output wire o_meie, + output wire o_mtie); localparam [1:0] CSR_SOURCE_CSR = 2'b00, @@ -47,6 +50,10 @@ module serv_csr reg mstatus_mie; reg mstatus_mpie; reg mie_mtie; + reg mie_meie; + + assign o_meie = mie_meie; + assign o_mtie = mie_mtie; reg mcause31; reg [3:0] mcause3_0; @@ -55,7 +62,7 @@ module serv_csr wire [B:0] csr_in; wire [B:0] csr_out; - reg timer_irq_r; + reg irq_r; wire [B:0] d = i_csr_d_sel ? i_csr_imm : i_rs1; @@ -81,7 +88,9 @@ module serv_csr assign o_q = csr_out; - wire timer_irq = i_mtip & mstatus_mie & mie_mtie; + wire timer_irq = i_mtip & mie_mtie; + wire ext_irq = i_meip & mie_meie; + wire irq = (timer_irq | ext_irq) & mstatus_mie; assign mcause = i_cnt0to3 ? mcause3_0[B:0] : //[3:0] i_cnt_done ? {mcause31,{B{1'b0}}} //[31] @@ -91,12 +100,15 @@ module serv_csr always @(posedge i_clk) begin if (i_trig_irq) begin - timer_irq_r <= timer_irq; - o_new_irq <= timer_irq & !timer_irq_r; + irq_r <= irq; + o_new_irq <= (irq & !irq_r); end if (i_mie_en & i_cnt7) - mie_mtie <= csr_in[B]; + mie_mtie <= csr_in[B]; + + if(i_mie_en & i_cnt11) + mie_meie <= csr_in[B]; /* The mie bit in mstatus gets updated under three conditions @@ -126,8 +138,8 @@ module serv_csr During an mcause CSR access function, they are assigned when bits 0 to 3 gets updated - During an external interrupt the exception code is set to - 7, since SERV only support timer interrupts + During an interrupt, the exception code is assigned to indicate + if it was caused by a timer interrupt (7) or an external interrupt (11). During an exception, the exception code is assigned to indicate if it was caused by an ebreak instruction (3), @@ -135,14 +147,15 @@ module serv_csr or misaligned jump (0) The expressions below are derived from the following truth table - irq => 0111 (timer=7) + timer_irq => 0111 (timer=7) + ext_irq => 1011 (ext=11) e_op => x011 (ebreak=3, ecall=11) mem => 01x0 (store=6, load=4) ctrl => 0000 (jump=0) */ if (i_mcause_en & i_en & i_cnt0to3 | (i_trap & i_cnt_done)) begin - mcause3_0[3] <= (i_e_op & !i_ebreak) | (!i_trap & csr_in[B]); - mcause3_0[2] <= o_new_irq | i_mem_op | (!i_trap & ((W == 1) ? mcause3_0[3] : csr_in[(W == 1) ? 0 : 2])); + mcause3_0[3] <= (o_new_irq & ext_irq) | (i_e_op & !i_ebreak) | (!i_trap & csr_in[B]); + mcause3_0[2] <= (o_new_irq & !ext_irq) | i_mem_op | (!i_trap & ((W == 1) ? mcause3_0[3] : csr_in[(W == 1) ? 0 : 2])); mcause3_0[1] <= o_new_irq | i_e_op | (i_mem_op & i_mem_cmd) | (!i_trap & ((W == 1) ? mcause3_0[2] : csr_in[(W == 1) ? 0 : 1])); mcause3_0[0] <= o_new_irq | i_e_op | (!i_trap & ((W == 1) ? mcause3_0[1] : csr_in[0])); end @@ -152,6 +165,7 @@ module serv_csr if (RESET_STRATEGY != "NONE") begin o_new_irq <= 1'b0; mie_mtie <= 1'b0; + mie_meie <= 1'b0; end end diff --git a/rtl/serv_debug.v b/rtl/serv_debug.v index af3c34f..7e59704 100644 --- a/rtl/serv_debug.v +++ b/rtl/serv_debug.v @@ -176,7 +176,7 @@ module serv_debug if (update_mcause) dbg_mcause <= dbg_csr; end - reg LUI, AUIPC, JAL, JALR, BEQ, BNE, BLT, BGE, BLTU, BGEU, LB, LH, LW, LBU, LHU, SB, SH, SW, ADDI, SLTI, SLTIU, XORI, ORI, ANDI,SLLI, SRLI, SRAI, ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND, FENCE, ECALL, EBREAK; + reg LUI, AUIPC, JAL, JALR, BEQ, BNE, BLT, BGE, BLTU, BGEU, LB, LH, LW, LBU, LHU, SB, SH, SW, ADDI, SLTI, SLTIU, XORI, ORI, ANDI,SLLI, SRLI, SRAI, ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND, FENCE, ECALL, EBREAK, WFI; reg CSRRW, CSRRS, CSRRC, CSRRWI, CSRRSI, CSRRCI; reg OTHER; @@ -222,6 +222,7 @@ module serv_debug FENCE <= 1'b0; ECALL <= 1'b0; EBREAK <= 1'b0; + WFI <= 1'b0; CSRRW <= 1'b0; CSRRS <= 1'b0; CSRRC <= 1'b0; @@ -273,6 +274,7 @@ module serv_debug 32'b???????_?????_?????_000_?????_00011_11 : FENCE <= 1'b1; 32'b0000000_00000_00000_000_00000_11100_11 : ECALL <= 1'b1; 32'b0000000_00001_00000_000_00000_11100_11 : EBREAK <= 1'b1; + 32'b0001000_00010_00000_000_00000_11100_11 : WFI <= 1'b1; 32'b???????_?????_?????_001_?????_11100_11 : CSRRW <= 1'b1; 32'b???????_?????_?????_010_?????_11100_11 : CSRRS <= 1'b1; 32'b???????_?????_?????_011_?????_11100_11 : CSRRC <= 1'b1; diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index 2aa5b8a..192f753 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -13,6 +13,7 @@ module serv_decode output reg o_cond_branch, output reg o_e_op, output reg o_ebreak, + output reg o_wfi, output reg o_branch_op, output reg o_shift_op, output reg o_rd_op, @@ -76,7 +77,7 @@ module serv_decode wire co_two_stage_op = ~opcode[2] | (funct3[0] & ~funct3[1] & ~opcode[0] & ~opcode[4]) | - (funct3[1] & ~funct3[2] & ~opcode[0] & ~opcode[4]) | co_mdu_op; + (funct3[1] & ~funct3[2] & ~opcode[0] & ~opcode[4]) | co_mdu_op | o_wfi; wire co_shift_op = (opcode[2] & ~funct3[1]) & !co_mdu_op; wire co_branch_op = opcode[4]; wire co_dbus_en = ~opcode[2] & ~opcode[4]; @@ -127,20 +128,22 @@ module serv_decode wire co_sh_right = funct3[2]; wire co_bne_or_bge = funct3[0]; - //Matches system ops except eceall/ebreak/mret + //Matches system ops except ecall/ebreak/mret/wfi wire csr_op = opcode[4] & opcode[2] & (|funct3); //op20 - wire co_ebreak = op20; + wire co_ebreak = op20 & !op22; + + wire co_wfi = opcode[4] & opcode[2] & op22 & !(|funct3); //opcode & funct3 & op21 wire co_ctrl_mret = opcode[4] & opcode[2] & op21 & !(|funct3); //Matches system opcodes except CSR accesses (funct3 == 0) - //and mret (!op21) - wire co_e_op = opcode[4] & opcode[2] & !op21 & !(|funct3); + //and mret (!op21) and wfi (!op22) + wire co_e_op = opcode[4] & opcode[2] & !op21 & !op22 & !(|funct3); //opcode & funct3 & imm30 @@ -255,6 +258,7 @@ module serv_decode o_two_stage_op = co_two_stage_op; o_e_op = co_e_op; o_ebreak = co_ebreak; + o_wfi = co_wfi; o_branch_op = co_branch_op; o_shift_op = co_shift_op; o_rd_op = co_rd_op; @@ -313,6 +317,7 @@ module serv_decode o_cond_branch <= co_cond_branch; o_e_op <= co_e_op; o_ebreak <= co_ebreak; + o_wfi <= co_wfi; o_two_stage_op <= co_two_stage_op; o_dbus_en <= co_dbus_en; o_mtval_pc <= co_mtval_pc; diff --git a/rtl/serv_rf_top.v b/rtl/serv_rf_top.v index 7b1c21a..b74dbe5 100644 --- a/rtl/serv_rf_top.v +++ b/rtl/serv_rf_top.v @@ -19,6 +19,10 @@ module serv_rf_top 0 : Register after the decoder. Faster but uses more resources 1 : (default) Register before the decoder. Slower but uses less resources */ + + parameter [0:0] EI = 0, + /* asd */ + parameter PRE_REGISTER = 1, /* Amount of reset applied to design "NONE" : No reset at all. Relies on a POR to set correct initialization @@ -36,6 +40,7 @@ module serv_rf_top input wire clk, input wire i_rst, input wire i_timer_irq, + input wire i_external_irq, `ifdef RISCV_FORMAL output wire rvfi_valid, output wire [63:0] rvfi_order, @@ -77,6 +82,11 @@ module serv_rf_top output wire [ 2:0] o_ext_funct3, input wire [31:0] i_ext_rd, input wire i_ext_ready, + + // Sleep functionality + output wire o_sleep_req, + output wire o_wakeup_req, + // MDU output wire o_mdu_valid); @@ -150,6 +160,7 @@ module serv_rf_top .WITH_CSR (WITH_CSR), .DEBUG (DEBUG), .MDU(MDU), + .EI(EI), .COMPRESSED(COMPRESSED), .ALIGN(ALIGN), .W(W)) @@ -158,6 +169,7 @@ module serv_rf_top .clk (clk), .i_rst (i_rst), .i_timer_irq (i_timer_irq), + .i_external_irq (i_external_irq), `ifdef RISCV_FORMAL .rvfi_valid (rvfi_valid ), .rvfi_order (rvfi_order ), @@ -208,6 +220,11 @@ module serv_rf_top .i_dbus_rdt (i_dbus_rdt), .i_dbus_ack (i_dbus_ack), + // Sleep + .o_sleep_req (o_sleep_req), + .o_wakeup_req (o_wakeup_req), + + //Extension .o_ext_funct3 (o_ext_funct3), .i_ext_ready (i_ext_ready), diff --git a/rtl/serv_sleep.v b/rtl/serv_sleep.v new file mode 100644 index 0000000..a6e68d6 --- /dev/null +++ b/rtl/serv_sleep.v @@ -0,0 +1,20 @@ +`default_nettype none +module serv_sleep + ( + input wire i_clk, + input wire i_rst, + input wire i_timer_irq, + input wire i_external_irq, + input wire i_cnt0, + input wire i_wfi, + input wire i_init, + input wire i_mtie, + input wire i_meie, + + output wire o_sleep_req, + output wire o_wakeup_req); + + assign o_sleep_req = i_wfi & i_cnt0 & i_init; + assign o_wakeup_req = (i_timer_irq & i_mtie) | (i_external_irq & i_meie); + +endmodule diff --git a/rtl/serv_synth_wrapper.v b/rtl/serv_synth_wrapper.v index d377688..613e617 100644 --- a/rtl/serv_synth_wrapper.v +++ b/rtl/serv_synth_wrapper.v @@ -86,7 +86,8 @@ module serv_synth_wrapper .PRE_REGISTER (PRE_REGISTER), .RESET_STRATEGY (RESET_STRATEGY), .WITH_CSR (WITH_CSR), - .MDU(1'b0)) + .MDU(1'b0), + .EI(1'b0)) cpu ( .clk (clk), diff --git a/rtl/serv_top.v b/rtl/serv_top.v index e41775e..c7cc7ab 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -9,12 +9,14 @@ module serv_top parameter RESET_PC = 32'd0, parameter [0:0] DEBUG = 1'b0, parameter [0:0] MDU = 1'b0, + parameter [0:0] EI = 1'b0, parameter [0:0] COMPRESSED=0, parameter [0:0] ALIGN = COMPRESSED) ( input wire clk, input wire i_rst, input wire i_timer_irq, + input wire i_external_irq, `ifdef RISCV_FORMAL output wire rvfi_valid, output wire [63:0] rvfi_order, @@ -70,6 +72,11 @@ module serv_top input wire [31:0] i_ext_rd, output wire [31:0] o_ext_rs1, output wire [31:0] o_ext_rs2, + + // Sleep functionality + output wire o_sleep_req, + output wire o_wakeup_req, + //MDU output wire o_mdu_valid); @@ -86,6 +93,7 @@ module serv_top wire two_stage_op; wire e_op; wire ebreak; + wire wfi; wire branch_op; wire shift_op; wire rd_op; @@ -183,6 +191,9 @@ module serv_top wire [31:0] wb_ibus_rdt; wire wb_ibus_ack; + wire mie_meie; + wire mie_mtie; + generate if (ALIGN) begin : gen_align serv_aligner align @@ -222,6 +233,20 @@ module serv_top end endgenerate + serv_sleep sleep + ( + .i_clk (clk), + .i_rst (i_rst), + .i_timer_irq (i_timer_irq), + .i_external_irq (i_external_irq), + .i_cnt0 (cnt0), + .i_wfi (wfi), + .i_init (init), + .o_sleep_req (o_sleep_req), + .o_wakeup_req (o_wakeup_req), + .i_meie (mie_meie), + .i_mtie (mie_mtie)); + serv_state #(.RESET_STRATEGY (RESET_STRATEGY), .WITH_CSR (WITH_CSR[0:0]), @@ -298,6 +323,7 @@ module serv_top .o_dbus_en (dbus_en), .o_e_op (e_op), .o_ebreak (ebreak), + .o_wfi (wfi), .o_branch_op (branch_op), .o_shift_op (shift_op), .o_rd_op (rd_op), @@ -563,6 +589,7 @@ module serv_top .i_cnt_done (cnt_done), .i_mem_op (!mtval_pc), .i_mtip (i_timer_irq), + .i_meip (i_external_irq), .i_trap (trap), .o_new_irq (new_irq), //Control @@ -580,7 +607,9 @@ module serv_top .o_csr_in (csr_in), .i_csr_imm (csr_imm), .i_rs1 (rs1), - .o_q (csr_rd)); + .o_q (csr_rd), + .o_meie (mie_meie), + .o_mtie (mie_mtie)); end else begin : gen_no_csr assign csr_in = {W{1'b0}}; assign csr_rd = {W{1'b0}}; diff --git a/serv.core b/serv.core index 3746a12..51c22b2 100644 --- a/serv.core +++ b/serv.core @@ -23,6 +23,7 @@ filesets: - rtl/serv_rf_top.v - rtl/serv_aligner.v - rtl/serv_compdec.v + - rtl/serv_sleep.v file_type : verilogSource openlane: @@ -30,6 +31,21 @@ filesets: - data/params.tcl : {file_type : tclSource} - rtl/serv_synth_wrapper.v : {file_type : verilogSource} + verilator_decoder_tb: + files: + - bench/decoder_sim.v + - "bench/decoder_tb.cpp" : {file_type : cppSource} + file_type : verilogSource + + + decoder_tb: + files: + - bench/decoder_sim.v + - bench/decoder_tb.v + file_type : verilogSource + depend : [vlog_tb_utils] + + targets: default: filesets : [core] @@ -37,6 +53,7 @@ targets: - "is_toplevel? (ALIGN)" - "is_toplevel? (COMPRESSED)" - "is_toplevel? (MDU)" + - "is_toplevel? (EI)" - "is_toplevel? (PRE_REGISTER)" - "is_toplevel? (RESET_STRATEGY)" - RISCV_FORMAL @@ -63,12 +80,29 @@ targets: filesets : [core, openlane] toplevel : serv_synth_wrapper + verilator_decoder_tb: + description: Verilator decoder testbench + filesets : [core, verilator_decoder_tb] + flow: sim + flow_options: + tool: verilator + verilator_options : [--trace] + parameters : + - RISCV_FORMAL + - "mdu? (MDU=1)" + toplevel : decoder_sim + parameters: MDU: datatype : int description: Enables interface for RISC-V standard M-extension paramtype : vlogparam + EI: + datatype : int + description : Enable interface for RISC-V external interrupts + paramtype : vlogparam + PRE_REGISTER: datatype : int description : Register signals before or after the decoder diff --git a/servant.core b/servant.core index fa641b3..8eb89d2 100644 --- a/servant.core +++ b/servant.core @@ -20,6 +20,22 @@ filesets: file_type : verilogSource depend : [vlog_tb_utils] + servant_timer_tb: + files: + - sw/timer_interrupt.hex : {file_type: user, copyto: .} + - bench/servant_timer_sim.v + - bench/servant_timer_tb.v + file_type : verilogSource + depend : [vlog_tb_utils] + + servant_external_tb: + files: + - sw/external_interrupt.hex : {file_type: user, copyto: .} + - bench/servant_external_sim.v + - bench/servant_external_tb.v + file_type : verilogSource + depend : [vlog_tb_utils] + service: files: - servant/ice40_pll.v @@ -37,7 +53,13 @@ filesets: - "!tool_quartus? (servant/servant_ram.v)" - servant/servant.v file_type : verilogSource - depend : [servile, "mdu? (mdu)"] + depend : [servile, "mdu? (mdu)", "ei? (ei)"] + + sleep_dummy: + files: + - servant/servant_sleep_dummy.v + file_type : verilogSource + depend : [servile] verilator_tb: files: @@ -47,6 +69,18 @@ filesets: file_type : verilogSource depend : ["vidbo? (vidbo)"] + verilator_timer_tb: + files: + - bench/servant_timer_sim.v + - "bench/servant_timer_tb.cpp" : {file_type : cppSource} + file_type : verilogSource + + verilator_external_tb: + files: + - bench/servant_external_sim.v + - "bench/servant_external_tb.cpp" : {file_type : cppSource} + file_type : verilogSource + # Target-specific filesets. Alphabetically sorted ac701: @@ -96,6 +130,19 @@ filesets: - servant/servant_cmod_a7.v : {file_type : verilogSource} - data/cmod_a7_35t.xdc : {file_type : xdc} + nexys_4: + files: + - servant/servant_nexys_4_clock_gen.v : {file_type : verilogSource} + - servant/servant_nexys_4.v : {file_type : verilogSource} + - data/nexys_4.xdc : {file_type : xdc} + + nexys_a7: + files: + - servant/servant_nexys_a7_clock_gen.v : {file_type : verilogSource} + - servant/servant_nexys_a7.v : {file_type : verilogSource} + - data/nexys_a7.xdc : {file_type : xdc} + + cyc1000: files: - data/cyc1000.sdc : {file_type : SDC} @@ -188,11 +235,11 @@ filesets: - data/nexys_2.tcl : {file_type : tclSource} - data/nexys_2.ucf : {file_type : UCF} - nexys_a7: - files: - - servant/servix_clock_gen.v : {file_type : verilogSource} - - servant/servix.v : {file_type : verilogSource} - - data/nexys_a7.xdc : {file_type : xdc} +# nexys_a7: +# files: +# - servant/servix_clock_gen.v : {file_type : verilogSource} +# - servant/servix.v : {file_type : verilogSource} +# - data/nexys_a7.xdc : {file_type : xdc} orangecrab: files: @@ -330,6 +377,24 @@ targets: parameters : [memfile, memsize] toplevel : servant_cmod_a7 + nexys_4: + description: Digilent Nexys 4 + filesets : [mem_files, soc, nexys_4] + flow: vivado + flow_options: + part : xc7a100tcsg324-1 + parameters : [memfile, memsize] + toplevel : servant_nexys_4 + + nexys_a7: + description: Digilent Nexys A7 + filesets : [mem_files, soc, nexys_a7] + flow: vivado + flow_options: + part : xc7a100tcsg324-1 + parameters : [memfile, memsize] + toplevel : servant_nexys_a7 + cyc1000: default_tool: quartus description: cyc1000 FPGA board @@ -536,15 +601,6 @@ targets: speed : -4 toplevel : servax - nexys_a7: - description: Digilent Nexys A7 - filesets : [mem_files, soc, nexys_a7] - flow: vivado - flow_options: - part : xc7a100tcsg324-1 - parameters : [memfile, memsize, frequency=32] - toplevel : servix - orangecrab_r0.2: default_tool: trellis description : OrangeCrab R0.2 @@ -588,6 +644,7 @@ targets: - RISCV_FORMAL - width - "mdu? (MDU=1)" + - "ei? (EI=1)" - SERV_CLEAR_RAM=true - firmware - memsize @@ -656,6 +713,7 @@ targets: parameters : - RISCV_FORMAL - "mdu? (MDU=1)" + - "ei? (EI=1)" - cps - firmware - memsize @@ -671,6 +729,54 @@ targets: - with_csr=1 toplevel : servant_sim + verilator_timer_tb: + description: Verilator timer testbench + filesets : [soc, sleep_dummy, verilator_timer_tb] + flow: sim + flow_options: + tool: verilator + verilator_options : [--trace] + parameters : + - RISCV_FORMAL + - "mdu? (MDU=1)" + - cps + - firmware + - memsize + - signature + - timeout + - trace_pc + - vcd + - vcd_start + - width + - compressed + - align + - with_csr=1 + toplevel : servant_timer_sim + + verilator_external_tb: + description: Verilator timer testbench + filesets : [soc, sleep_dummy, verilator_external_tb] + flow: sim + flow_options: + tool: verilator + verilator_options : [--trace] + parameters : + - RISCV_FORMAL + - "mdu? (MDU=1)" + - cps + - firmware + - memsize + - signature + - timeout + - trace_pc + - vcd + - vcd_start + - width + - compressed + - align + - with_csr=1 + toplevel : servant_external_sim + zcu106: default_tool: vivado description : Zynq UltraScale+ MPSoC ZCU106 Evaluation Kit @@ -711,6 +817,11 @@ parameters: description : Enables RISC-V standard M-extension paramtype : vlogdefine + EI: + datatype : int + description : Enable interface for RISC-V external interrupts + paramtype : vlogdefine + memfile: datatype : file description : Preload RAM with a hex file at compile-time diff --git a/servant/servant.v b/servant/servant.v index fb027df..8508ad9 100644 --- a/servant/servant.v +++ b/servant/servant.v @@ -2,8 +2,11 @@ module servant ( input wire wb_clk, + input wire main_clk, input wire wb_rst, - output wire q); + input wire ext_irq, + output wire q, + output reg o_sleep); parameter memfile = "zephyr_hello.hex"; parameter memsize = 8192; @@ -22,6 +25,12 @@ module servant localparam [0:0] with_mdu = 1'b0; `endif +`ifdef EI + localparam [0:0] with_ei = 1'b1; +`else + localparam [0:0] with_ei = 1'b0; +`endif + localparam aw = $clog2(memsize); localparam csr_regs = with_csr*4; @@ -63,6 +72,8 @@ module servant wire [rf_l2d-1:0] rf_raddr; wire rf_ren; wire [rf_width-1:0] rf_rdata; + wire sleep_req; + wire wakeup_req; servant_mux servant_mux ( @@ -106,7 +117,7 @@ module servant #(.RESET_STRATEGY (reset_strategy), .WIDTH (32)) timer - (.i_clk (wb_clk), + (.i_clk (main_clk), .i_rst (wb_rst), .o_irq (timer_irq), .i_wb_cyc (wb_timer_stb), @@ -140,12 +151,16 @@ module servant .debug (debug), .with_c (compress[0]), .with_csr (with_csr[0]), - .with_mdu (with_mdu)) + .with_mdu (with_mdu), + .with_ei (with_ei)) cpu ( .i_clk (wb_clk), .i_rst (wb_rst), .i_timer_irq (timer_irq), + .i_external_irq (ext_irq), + .o_wakeup_req (wakeup_req), + .o_sleep_req (sleep_req), .o_wb_mem_adr (wb_mem_adr), .o_wb_mem_dat (wb_mem_dat), @@ -170,4 +185,13 @@ module servant .o_rf_ren (rf_ren), .i_rf_rdata (rf_rdata)); + always @(posedge main_clk) begin + if (sleep_req) + o_sleep <= 1; + if (wakeup_req) + o_sleep <= 0; + if (wb_rst) + o_sleep <= 0; + end + endmodule diff --git a/servant/servant_nexys_4.v b/servant/servant_nexys_4.v new file mode 100644 index 0000000..6f2e941 --- /dev/null +++ b/servant/servant_nexys_4.v @@ -0,0 +1,53 @@ +`default_nettype none +module servant_nexys_4 + ( + input wire i_clk, + input wire i_rst, + input wire ext_irq, + output wire o_uart_tx, + output wire q); + + parameter memfile = "zephyr_hello.hex"; + parameter memsize = 8192; + + wire main_clk; + wire wb_clk; + wire wb_rst; + + wire sleep_req; + wire wakeup_req; + + reg sleep; + + always @(posedge main_clk) begin + if (sleep_req) + sleep <= 0; + if (wakeup_req) + sleep <= 1; + if (wb_rst) + sleep <= 1; + end + + assign o_uart_tx = q; + + servant_nexys_4_clock_gen + clock_gen + (.i_clk (i_clk), + .i_rst (i_rst), + .i_clk0_en (sleep), + .i_clk1_en (1), + .o_clk0 (main_clk), + .o_clk1 (wb_clk), + .o_rst (wb_rst)); + servant + #(.memfile (memfile), + .memsize (memsize)) + servant + (.wb_clk (wb_clk), + .wb_rst (wb_rst), + .q (q), + .o_sleep_req (sleep_req), + .o_wakeup_req (wakeup_req)); + +endmodule +`default_nettype wire diff --git a/servant/servant_nexys_4_clock_gen.v b/servant/servant_nexys_4_clock_gen.v new file mode 100644 index 0000000..5ff4c58 --- /dev/null +++ b/servant/servant_nexys_4_clock_gen.v @@ -0,0 +1,54 @@ +`default_nettype none +module servant_nexys_4_clock_gen + (input wire i_clk, + input wire i_rst, + input wire i_clk0_en, + input wire i_clk1_en, + output wire o_clk0, + output wire o_clk1, + output reg o_rst); + + wire clkfb; + wire locked; + wire pll_rst; + wire pll_clk; + reg locked_r; + + assign pll_rst = !i_rst; + + MMCME2_BASE + #(.CLKIN1_PERIOD (10), //100MHz + + /* Set VCO frequency to 100*8.0=800 MHz + Allowed values are 2.0 to 64.0. Resulting VCO freq + needs to be 600-1200MHz */ + .CLKFBOUT_MULT_F (8.000), + + .CLKOUT0_DIVIDE_F (25.000)) // 800/25 = 32 MHz + pll + (.CLKIN1 (i_clk), + .RST (pll_rst), + .CLKOUT0 (pll_clk), + .LOCKED (locked), + .CLKFBOUT (clkfb), + .CLKFBIN (clkfb)); + + always @(posedge pll_clk) begin + locked_r <= locked; + o_rst <= !locked_r; + end + + BUFGCE clk0_buf + ( + .I (pll_clk), + .O (o_clk0), + .CE (i_clk0_en)); + + BUFGCE clk1_buf + ( + .I (pll_clk), + .O (o_clk1), + .CE (i_clk0_en)); + +endmodule +`default_nettype wire diff --git a/servant/servant_nexys_a7.v b/servant/servant_nexys_a7.v new file mode 100644 index 0000000..7412c62 --- /dev/null +++ b/servant/servant_nexys_a7.v @@ -0,0 +1,42 @@ +`default_nettype none +module servant_nexys_a7 + ( + input wire i_clk, + input wire i_rst, + input wire ext_irq, + output wire o_uart_tx, + output wire q); + + parameter memfile = "zephyr_hello.hex"; + parameter memsize = 8192; + + wire main_clk; + wire wb_clk; + wire wb_rst; + wire sleep; + + assign o_uart_tx = q; + + servant_nexys_a7_clock_gen + clock_gen + (.i_clk (i_clk), + .i_rst (i_rst), + .i_clk0_en (1), + .i_clk1_en (!sleep), + .o_clk0 (main_clk), + .o_clk1 (wb_clk), + .o_rst (wb_rst)); + + servant + #(.memfile (memfile), + .memsize (memsize)) + servant + (.wb_clk (wb_clk), + .main_clk (main_clk), + .wb_rst (wb_rst), + .q (q), + .ext_irq (ext_irq), + .o_sleep (sleep)); + +endmodule +`default_nettype wire diff --git a/servant/servant_nexys_a7_clock_gen.v b/servant/servant_nexys_a7_clock_gen.v new file mode 100644 index 0000000..8ca366f --- /dev/null +++ b/servant/servant_nexys_a7_clock_gen.v @@ -0,0 +1,57 @@ +`default_nettype none +module servant_nexys_a7_clock_gen + (input wire i_clk, + input wire i_rst, + input wire i_clk0_en, + input wire i_clk1_en, + output wire o_clk0, + output wire o_clk1, + output reg o_rst); + + wire clkfb; + wire locked; + wire pll_rst; + wire pll_clk; + reg locked_r; + + assign pll_rst = !i_rst; + + MMCME2_BASE + #(.CLKIN1_PERIOD (10), //100MHz + + /* Set VCO frequency to 100*8.0=800 MHz + Allowed values are 2.0 to 64.0. Resulting VCO freq + needs to be 600-1200MHz */ + .CLKFBOUT_MULT_F (6.000), + + .CLKOUT0_DIVIDE_F (64.000)) // 800/25 = 32 MHz + pll + (.CLKIN1 (i_clk), + .RST (pll_rst), + .CLKOUT0 (pll_clk), + .LOCKED (locked), + .CLKFBOUT (clkfb), + .CLKFBIN (clkfb)); + + always @(posedge pll_clk) begin + locked_r <= locked; + o_rst <= !locked_r; + end + + BUFGCE clk0_buf + ( + .I (pll_clk), + .O (o_clk0), + .CE (i_clk0_en)); + + BUFGCE clk1_buf + ( + .I (pll_clk), + .O (o_clk1), + .CE (i_clk1_en)); + + + + +endmodule +`default_nettype wire diff --git a/servant/servant_sleep_dummy.v b/servant/servant_sleep_dummy.v new file mode 100644 index 0000000..894d2f2 --- /dev/null +++ b/servant/servant_sleep_dummy.v @@ -0,0 +1,47 @@ +`default_nettype none +module servant_sleep_dummy + ( + input wire i_clk, + input wire i_rst, + input wire ext_irq, + output wire q); + + parameter memfile = "zephyr_hello.hex"; + parameter memsize = 8192; + parameter reset_strategy = "MINI"; + parameter width = 1; + parameter sim = 0; + parameter [0:0] debug = 1'b0; + parameter with_csr = 1; + parameter [0:0] compress = 0; + parameter [0:0] align = compress; + + + wire sleep; + wire wb_clk; + wire sleep_req; + wire wakeup_req; + + assign wb_clk = i_clk & !sleep; + + servant #( + .memfile(memfile), + .memsize(memsize), + .reset_strategy(reset_strategy), + .width(width), + .sim(sim), + .debug(debug), + .with_csr(with_csr), + .compress(compress), + .align(align) + ) + servant ( + .wb_clk (wb_clk), + .main_clk (i_clk), + .wb_rst (i_rst), + .ext_irq (ext_irq), + .q (q), + .o_sleep (sleep)); + +endmodule +`default_nettype wire diff --git a/servile/servile.v b/servile/servile.v index 64abc4a..6aa4d49 100644 --- a/servile/servile.v +++ b/servile/servile.v @@ -16,6 +16,7 @@ module servile parameter [0:0] debug = 1'b0, parameter [0:0] with_c = 1'b0, parameter [0:0] with_csr = 1'b0, + parameter [0:0] with_ei = 1'b0, parameter [0:0] with_mdu = 1'b0, //Internally calculated. Do not touch parameter B = width-1, @@ -25,6 +26,10 @@ module servile input wire i_clk, input wire i_rst, input wire i_timer_irq, + input wire i_external_irq, + + output wire o_sleep_req, + output wire o_wakeup_req, //Memory (WB) interface output wire [31:0] o_wb_mem_adr, @@ -96,6 +101,8 @@ module servile wire [31:0] mdu_rd; wire mdu_ready; + wire ei_irq; + servile_mux #(.sim (sim)) mux @@ -194,7 +201,16 @@ module servile end else begin assign mdu_ready = 1'b0; assign mdu_rd = 32'd0; - end + end // else: !if(with_mdu) + + if (with_ei) begin : gen_ei + ei_top ei_serv + (.i_clk (i_clk), + .i_rst (i_rst), + .o_eirq (ei_irq)); + end else begin + assign ei_irq = 1'b0; + end // else !if(with_ei) endgenerate serv_top @@ -206,12 +222,15 @@ module servile .RESET_PC (reset_pc), .DEBUG (debug), .MDU (with_mdu), + .EI (with_ei), .COMPRESSED (with_c)) cpu ( .clk (i_clk), .i_rst (i_rst), .i_timer_irq (i_timer_irq), + .i_external_irq (i_external_irq), + `ifdef RISCV_FORMAL .rvfi_valid (), @@ -266,6 +285,10 @@ module servile .i_dbus_rdt (wb_dbus_rdt), .i_dbus_ack (wb_dbus_ack), + // Sleep + .o_sleep_req (o_sleep_req), + .o_wakeup_req (o_wakeup_req), + //Extension IF .o_ext_rs1 (mdu_rs1), .o_ext_rs2 (mdu_rs2), diff --git a/sw/Makefile b/sw/Makefile index f82ddc6..71d5e12 100644 --- a/sw/Makefile +++ b/sw/Makefile @@ -1,9 +1,11 @@ TOOLCHAIN_PREFIX?=riscv64-unknown-elf- +CC?=$(TOOLCHAIN_PREFIX)gcc +OBJCOPY=$(TOOLCHAIN_PREFIX)objcopy %.elf: %.S link.ld - $(TOOLCHAIN_PREFIX)gcc -nostartfiles -nostdlib -march=rv32i_zicsr -mabi=ilp32 -Tlink.ld -o$@ $< + $(CC) -nostartfiles -nostdlib -march=rv32i_zicsr -mabi=ilp32 -Tlink.ld -o$@ $< %.bin: %.elf - $(TOOLCHAIN_PREFIX)objcopy -O binary $< $@ + $(OBJCOPY) -O binary $< $@ %.hex: %.bin python3 makehex.py $< > $@ diff --git a/sw/external_interrupt.S b/sw/external_interrupt.S new file mode 100644 index 0000000..c5f9ee3 --- /dev/null +++ b/sw/external_interrupt.S @@ -0,0 +1,18 @@ + .section .text + .global _start + .global handler +_start: + la t0, handler + csrw mtvec, t0 + li t0, 0x8 + csrrs x0, mstatus, t0 + li t0, 0x800 + csrrs x0, mie, t0 +loop: + j loop +handler: + li t0, 0x8 + csrrc x0, mstatus, t0 + li t0, 0x800 + csrrc x0, mie, t0 + mret diff --git a/sw/external_interrupt.hex b/sw/external_interrupt.hex new file mode 100644 index 0000000..37b68ff --- /dev/null +++ b/sw/external_interrupt.hex @@ -0,0 +1,16 @@ +00000297 +02428293 +30529073 +00800293 +3002A073 +00100293 +00B29293 +3042A073 +0000006F +00800293 +3002B073 +00100293 +00B29293 +3042B073 +30200073 +00000000 diff --git a/sw/external_interrupt_wfi.S b/sw/external_interrupt_wfi.S new file mode 100644 index 0000000..6d26ed3 --- /dev/null +++ b/sw/external_interrupt_wfi.S @@ -0,0 +1,17 @@ + .section .text + .global _start + .global handler +_start: + la t0, handler + csrw mtvec, t0 + li t0, 0x8 + csrrs x0, mstatus, t0 + li t0, 0x800 + csrrs x0, mie, t0 + wfi +handler: + li t0, 0x8 + csrrc x0, mstatus, t0 + li t0, 0x800 + csrrc x0, mie, t0 + mret diff --git a/sw/external_interrupt_wfi.hex b/sw/external_interrupt_wfi.hex new file mode 100644 index 0000000..a905c30 --- /dev/null +++ b/sw/external_interrupt_wfi.hex @@ -0,0 +1,16 @@ +00000297 +02428293 +30529073 +00800293 +3002A073 +00100293 +00B29293 +3042A073 +10500073 +00800293 +3002B073 +00100293 +00B29293 +3042B073 +30200073 +00000000 diff --git a/sw/timer_interrupt.S b/sw/timer_interrupt.S new file mode 100644 index 0000000..dbc92c5 --- /dev/null +++ b/sw/timer_interrupt.S @@ -0,0 +1,22 @@ + .section .text + .global _start + .global handler + .equ DELAY, 1500 +_start: + la t0, handler + csrw mtvec, t0 + li t0, 0x80000000 + li t1, DELAY + sw t1, 0(t0) + li t0, 0x8 + csrrs x0, mstatus, t0 + li t0, 0x80 + csrrs x0, mie, t0 +wait: + j wait +handler: + li t0, 0x8 + csrrc x0, mstatus, t0 + li t0, 0x80 + csrrc x0, mie, t0 + mret diff --git a/sw/timer_interrupt.hex b/sw/timer_interrupt.hex new file mode 100644 index 0000000..1b0a2a5 --- /dev/null +++ b/sw/timer_interrupt.hex @@ -0,0 +1,17 @@ +00000297 +02C28293 +30529073 +800002B7 +5DC00313 +0062A023 +00800293 +3002A073 +08000293 +3042A073 +0000006F +00800293 +3002B073 +08000293 +3042B073 +30200073 +00000000 diff --git a/sw/timer_interrupt_wfi.hex b/sw/timer_interrupt_wfi.hex new file mode 100644 index 0000000..54c22e3 --- /dev/null +++ b/sw/timer_interrupt_wfi.hex @@ -0,0 +1,17 @@ +00000297 +02C28293 +30529073 +800002B7 +5DC00313 +0062A023 +00800293 +3002A073 +08000293 +3042A073 +10500073 +00800293 +3002B073 +08000293 +3042B073 +30200073 +00000000 diff --git a/sw/timer_interrupt_wfi_recurring.S b/sw/timer_interrupt_wfi_recurring.S new file mode 100644 index 0000000..fb43c2a --- /dev/null +++ b/sw/timer_interrupt_wfi_recurring.S @@ -0,0 +1,29 @@ + .section .text + .global _start + .global handler + .equ DELAY, 8000 +_start: + la t0, handler + csrw mtvec, t0 + + li t0, 0x80000000 + li s2, DELAY + sw s2, 0(t0) + + li t0, 0x8 + csrrs x0, mstatus, t0 + li t0, 0x80 + csrrs x0, mie, t0 +wait: + wfi + j wait + +handler: + + li t1, DELAY + add s2, t1, s2 + + li t0, 0x80000000 + sw s2, 0(t0) + + mret