diff --git a/project.cfg b/project.cfg index dda609e..66aa083 100644 --- a/project.cfg +++ b/project.cfg @@ -22,8 +22,8 @@ package = tqg144 speedgrade = -2 toplevel = top_generic xst_opts = -vlgincdir rtl/util -files_verilog = rtl/util/conv.vh - rtl/toplevel/top_generic.v +files_verilog = rtl/toplevel/top_generic.v + rtl/util/conv.vh rtl/core/nco_q15.v rtl/core/sigmadelta_sampler.v rtl/core/sigmadelta_rcmodel_q15.v @@ -31,8 +31,14 @@ files_verilog = rtl/util/conv.vh rtl/core/mul_const.v rtl/core/lpf_iir_q15_k.v rtl/core/decimate_by_r_q15.v + rtl/core/mcu_peripherals.v + rtl/core/mcu.v + rtl/core/mem_jtag_writable.v + # Arch rtl/arch/spartan-6/lvds_comparator.v rtl/arch/spartan-6/clk_gen.v + rtl/arch/spartan-6/jtag_if.v + # SERV rtl/serv/serv_aligner.v rtl/serv/serv_alu.v rtl/serv/serv_bufreg.v @@ -47,25 +53,104 @@ files_verilog = rtl/util/conv.vh rtl/serv/serv_rf_if.v rtl/serv/serv_rf_ram_if.v rtl/serv/serv_rf_ram.v - rtl/serv/serv_rf_top.v rtl/serv/serv_state.v + rtl/serv/serv_rf_top.v rtl/serv/serv_synth_wrapper.v rtl/serv/serv_top.v + # QERV + # rtl/qerv/serv_rf_top.v + # rtl/qerv/serv_synth_wrapper.v + # rtl/qerv/serv_top.v + # rtl/qerv/qerv_immdec.v + # Servile rtl/serv/servile_arbiter.v rtl/serv/servile_mux.v rtl/serv/servile_rf_mem_if.v rtl/serv/servile.v - rtl/serv/serving_ram.v - rtl/serv/serving.v - rtl/arch/spartan-6/jtag_if.v + # rtl/qerv/servile_arbiter.v + # rtl/qerv/servile_mux.v + # rtl/qerv/servile_rf_mem_if.v + # rtl/qerv/servile.v + # WB rtl/wb/wb_gpio.v rtl/wb/wb_gpio_banks.v + rtl/wb/wb_mux.v rtl/wb/jtag_wb_bridge.v - rtl/core/mcu.v + rtl/wb/wb_timer.v + files_con = boards/mimas_v1/constraints.ucf files_other = rtl/util/rc_alpha_q15.vh rtl/util/clog2.vh - sw/blinky/blinky.hex + sw/sweep/sweep.hex + +[target.synth_sim] +toolchain = iverilog +runtime = all +toplevel = tb_top_generic +ivl_opts = -Irtl/util +files_verilog = rtl/toplevel/top_generic.v + rtl/util/conv.vh + rtl/core/nco_q15.v + rtl/core/sigmadelta_sampler.v + rtl/core/sigmadelta_rcmodel_q15.v + rtl/core/sigmadelta_input_q15.v + rtl/core/mul_const.v + rtl/core/lpf_iir_q15_k.v + rtl/core/decimate_by_r_q15.v + rtl/core/mcu_peripherals.v + rtl/core/mcu.v + rtl/core/mem_jtag_writable.v + # Arch + rtl/core/lvds_comparator.v + sim/overrides/clk_gen.v + rtl/core/jtag_if.v + # SERV + rtl/serv/serv_aligner.v + rtl/serv/serv_alu.v + rtl/serv/serv_bufreg.v + rtl/serv/serv_bufreg2.v + rtl/serv/serv_compdec.v + rtl/serv/serv_csr.v + rtl/serv/serv_ctrl.v + rtl/serv/serv_debug.v + rtl/serv/serv_decode.v + rtl/serv/serv_immdec.v + rtl/serv/serv_mem_if.v + rtl/serv/serv_rf_if.v + rtl/serv/serv_rf_ram_if.v + rtl/serv/serv_rf_ram.v + rtl/serv/serv_state.v + rtl/serv/serv_rf_top.v + rtl/serv/serv_synth_wrapper.v + rtl/serv/serv_top.v + # QERV + # rtl/qerv/serv_rf_top.v + # rtl/qerv/serv_synth_wrapper.v + # rtl/qerv/serv_top.v + # rtl/qerv/qerv_immdec.v + # Servile + rtl/serv/servile_arbiter.v + rtl/serv/servile_mux.v + rtl/serv/servile_rf_mem_if.v + rtl/serv/servile.v + # rtl/qerv/servile_arbiter.v + # rtl/qerv/servile_mux.v + # rtl/qerv/servile_rf_mem_if.v + # rtl/qerv/servile.v + # WB + rtl/wb/wb_gpio.v + rtl/wb/wb_gpio_banks.v + rtl/wb/wb_mux.v + rtl/wb/jtag_wb_bridge.v + rtl/wb/wb_timer.v + + sim/tb/tb_top_generic.v + +files_con = boards/mimas_v1/constraints.ucf +files_other = rtl/util/rc_alpha_q15.vh + rtl/util/clog2.vh + rtl/util/conv.vh + sw/sweep/sweep.hex [target.jtag] toolchain = ISE @@ -93,6 +178,13 @@ files_verilog = sim/tb/tb_svf.v rtl/core/cdc_strobed.v files_other = sim/other/test.svf +[target.tb_wb_timer] +toolchain = iverilog +runtime = all +toplevel = tb_wb_timer +files_verilog = sim/tb/tb_wb_timer.v + rtl/wb/wb_timer.v + [target.tools] toolchain = make output_files = tools/test diff --git a/rtl/core/mcu.v b/rtl/core/mcu.v index ace5a1a..ca97279 100644 --- a/rtl/core/mcu.v +++ b/rtl/core/mcu.v @@ -16,7 +16,8 @@ module mcu #( output wire [31:0] o_GPO_A, output wire [31:0] o_GPO_B, output wire [31:0] o_GPO_C, - output wire [31:0] o_GPO_D + output wire [31:0] o_GPO_D, + output wire o_test ); localparam WITH_CSR = 1; localparam regs = 32+WITH_CSR*4; @@ -26,7 +27,8 @@ module mcu #( wire rst_mem_reason; wire timer_irq; assign rst = i_rst | rst_mem_reason; - assign timer_irq = 1'b0; + + assign o_test = timer_irq; // Busses // CPU->memory @@ -72,9 +74,6 @@ module mcu #( assign GPI[32*3-1:32*2] = i_GPI_C; assign GPI[32*4-1:32*3] = i_GPI_D; - assign wb_ext_ack = wb_ext_gpio_ack; - assign wb_ext_rdt = wb_ext_gpio_rdt; - // SERV core with mux splitting dbus into mem and ext and // arbiter combining mem and ibus // separate rst line to let other hardware keep core under reset @@ -89,7 +88,7 @@ module mcu #( ) servile ( .i_clk(i_clk), .i_rst(rst), - .i_timer_irq(timer_irq), + .i_timer_irq(1'b0), //timer_irq), //Memory interface .o_wb_mem_adr(wb_mem_adr), @@ -119,14 +118,14 @@ module mcu #( ); // WB arbiter combining RF and mem interfaces into 1 - // Last 128 bytes are used for registers + // Last 128 bytes are used for registers servile_rf_mem_if #( .depth(memsize), .rf_regs(regs) ) rf_mem_if ( .i_clk (i_clk), - .i_rst (i_rst), - + .i_rst (rst), + .i_waddr(rf_waddr), .i_wdata(rf_wdata), .i_wen(rf_wen), @@ -165,111 +164,20 @@ module mcu #( .o_core_reset(rst_mem_reason) ); - wb_gpio_banks #( - .BASE_ADDR(32'h40000000), - .NUM_BANKS(4) - ) gpio ( - .i_wb_clk(i_clk), - .i_wb_rst(rst), - .i_wb_dat(wb_ext_dat), + mcu_peripherals peripherals ( + .i_clk(i_clk), + .i_rst(rst), .i_wb_adr(wb_ext_adr), + .i_wb_dat(wb_ext_dat), + .i_wb_sel(wb_ext_sel), .i_wb_we(wb_ext_we), .i_wb_stb(wb_ext_stb), - .i_wb_sel(wb_ext_sel), .o_wb_rdt(wb_ext_rdt), .o_wb_ack(wb_ext_ack), + // Peripheral IO .i_gpio(GPI), - .o_gpio(GPO) + .o_gpio(GPO), + .o_timer_irq(timer_irq) ); -endmodule - -module memory #( - parameter memfile = "", - parameter depth = 256, - parameter sim = 1'b0, - localparam aw = `CLOG2(depth) -)( - input wire i_clk, - input wire i_rst, - input wire [aw-1:0] i_waddr, - input wire [7:0] i_wdata, - input wire i_wen, - input wire [aw-1:0] i_raddr, - output reg [7:0] o_rdata, - output wire o_core_reset -); - // The actual memory - reg [7:0] mem [0:depth-1]; - wire [aw-1:0] mem_adr; - assign mem_adr = (i_wen==1'b1) ? i_waddr : - i_raddr; - - // Second port wishbone - wire [31:0] wb_adr; - wire [31:0] wb_dat; - reg [31:0] wb_rdt; - wire [3:0] wb_sel; - wire wb_cyc; - wire wb_we; - wire wb_stb; - reg wb_ack; - reg wb_req_d; - wire cmd_reset; - // Driven by JTAG - jtag_wb_bridge #( - .chain(1), - .byte_aligned(1) - ) jtag_wb ( - .i_clk(i_clk), - .i_rst(i_rst), - - .o_wb_adr(wb_adr), - .o_wb_dat(wb_dat), - .o_wb_sel(wb_sel), - .o_wb_we(wb_we), - .o_wb_cyc(wb_cyc), - .o_wb_stb(wb_stb), - .i_wb_rdt(wb_rdt), - .i_wb_ack(wb_ack), - .o_cmd_reset(cmd_reset) - ); - - assign o_core_reset = cmd_reset; - - // Read/Write - always @(posedge i_clk) begin - if (i_rst) begin - wb_req_d <= 1'b0; - wb_ack <= 1'b0; - wb_rdt <= 32'h00000000; - o_rdata <= 32'h00000000; - end else begin - if (i_wen) - mem[mem_adr] <= i_wdata; - o_rdata <= mem[mem_adr]; - - wb_req_d <= wb_stb && wb_cyc; - wb_ack <= wb_req_d; - if (wb_we && wb_stb && wb_cyc) - mem[wb_adr[aw-1:0]] <= wb_dat[7:0]; - wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]}; - end - end - - // Preload memory - integer i; - initial begin - if(sim==1'b1) begin - for (i = 0; i < depth; i = i + 1) - mem[i] = 8'h00; - end - if(|memfile) begin - $display("Preloading %m from %s", memfile); - $readmemh(memfile, mem); - end - end - - - -endmodule +endmodule \ No newline at end of file diff --git a/rtl/core/mcu_peripherals.v b/rtl/core/mcu_peripherals.v new file mode 100644 index 0000000..0877196 --- /dev/null +++ b/rtl/core/mcu_peripherals.v @@ -0,0 +1,123 @@ +`timescale 1ns/1ps + +module mcu_peripherals ( + input wire i_clk, + input wire i_rst, + + input wire [31:0] i_wb_adr, + input wire [31:0] i_wb_dat, + input wire [3:0] i_wb_sel, + input wire i_wb_we, + input wire i_wb_stb, + output wire [31:0] o_wb_rdt, + output wire o_wb_ack, + + input wire [4*32-1:0] i_gpio, + output wire [4*32-1:0] o_gpio, + output wire o_timer_irq +); + localparam [31:0] GPIO_BASE_ADDR = 32'h4000_0000; + localparam [31:0] GPIO_ADDR_MASK = 32'hFFFF_0000; + localparam [31:0] TIMER_BASE_ADDR = 32'h4001_0000; + localparam [31:0] TIMER_ADDR_MASK = 32'hFFFF_0000; + + wire [2*32-1:0] wbs_adr; + wire [2*32-1:0] wbs_dat_w; + wire [2*4-1:0] wbs_sel; + wire [1:0] wbs_we; + wire [1:0] wbs_cyc; + wire [1:0] wbs_stb; + wire [2*3-1:0] wbs_cti; + wire [2*2-1:0] wbs_bte; + wire [2*32-1:0] wbs_dat_r; + wire [1:0] wbs_ack; + + wire [31:0] gpio_wbs_adr = wbs_adr[0*32 +: 32]; + wire [31:0] gpio_wbs_dat_w = wbs_dat_w[0*32 +: 32]; + wire [3:0] gpio_wbs_sel = wbs_sel[0*4 +: 4]; + wire gpio_wbs_we = wbs_we[0]; + wire gpio_wbs_cyc = wbs_cyc[0]; + wire gpio_wbs_stb = wbs_stb[0]; + wire [31:0] gpio_wbs_dat_r; + wire gpio_wbs_ack; + + wire [31:0] timer_wbs_dat_w = wbs_dat_w[1*32 +: 32]; + wire timer_wbs_we = wbs_we[1]; + wire timer_wbs_cyc = wbs_cyc[1]; + wire timer_wbs_stb = wbs_stb[1]; + wire [31:0] timer_wbs_dat_r; + wire timer_wbs_ack; + + wb_mux #( + .dw(32), + .aw(32), + .num_slaves(2), + .MATCH_ADDR({TIMER_BASE_ADDR, GPIO_BASE_ADDR}), + .MATCH_MASK({TIMER_ADDR_MASK, GPIO_ADDR_MASK}) + ) ext_mux ( + .wb_clk_i(i_clk), + .wb_rst_i(i_rst), + + .wbm_adr_i(i_wb_adr), + .wbm_dat_i(i_wb_dat), + .wbm_sel_i(i_wb_sel), + .wbm_we_i(i_wb_we), + .wbm_cyc_i(i_wb_stb), + .wbm_stb_i(i_wb_stb), + .wbm_cti_i(3'b000), + .wbm_bte_i(2'b00), + .wbm_dat_o(o_wb_rdt), + .wbm_ack_o(o_wb_ack), + .wbm_err_o(), + .wbm_rty_o(), + + .wbs_adr_o(wbs_adr), + .wbs_dat_o(wbs_dat_w), + .wbs_sel_o(wbs_sel), + .wbs_we_o(wbs_we), + .wbs_cyc_o(wbs_cyc), + .wbs_stb_o(wbs_stb), + .wbs_cti_o(wbs_cti), + .wbs_bte_o(wbs_bte), + .wbs_dat_i(wbs_dat_r), + .wbs_ack_i(wbs_ack), + .wbs_err_i(2'b00), + .wbs_rty_i(2'b00) + ); + + wb_gpio_banks #( + .BASE_ADDR(GPIO_BASE_ADDR), + .NUM_BANKS(4) + ) gpio ( + .i_wb_clk(i_clk), + .i_wb_rst(i_rst), + .i_wb_dat(gpio_wbs_dat_w), + .i_wb_adr(gpio_wbs_adr), + .i_wb_we(gpio_wbs_we), + .i_wb_stb(gpio_wbs_stb & gpio_wbs_cyc), + .i_wb_sel(gpio_wbs_sel), + .o_wb_rdt(gpio_wbs_dat_r), + .o_wb_ack(gpio_wbs_ack), + .i_gpio(i_gpio), + .o_gpio(o_gpio) + ); + + assign wbs_dat_r[0*32 +: 32] = gpio_wbs_dat_r; + assign wbs_ack[0] = gpio_wbs_ack; + + wb_countdown_timer timer ( + .i_clk(i_clk), + .i_rst(i_rst), + .o_irq(o_timer_irq), + .i_wb_dat(timer_wbs_dat_w), + .o_wb_dat(timer_wbs_dat_r), + .i_wb_we(timer_wbs_we), + .i_wb_cyc(timer_wbs_cyc), + .i_wb_stb(timer_wbs_stb), + .o_wb_ack(timer_wbs_ack) + ); + + + assign wbs_dat_r[1*32 +: 32] = timer_wbs_dat_r; + assign wbs_ack[1] = timer_wbs_ack; +endmodule diff --git a/rtl/core/mem_jtag_writable.v b/rtl/core/mem_jtag_writable.v new file mode 100644 index 0000000..17b27bf --- /dev/null +++ b/rtl/core/mem_jtag_writable.v @@ -0,0 +1,91 @@ +`timescale 1ns/1ps + +module memory #( + parameter memfile = "", + parameter depth = 256, + parameter sim = 1'b0, + parameter aw = `CLOG2(depth) + )( + input wire i_clk, + input wire i_rst, + input wire [aw-1:0] i_waddr, + input wire [7:0] i_wdata, + input wire i_wen, + input wire [aw-1:0] i_raddr, + output reg [7:0] o_rdata, + output wire o_core_reset +); + // The actual memory + reg [7:0] mem [0:depth-1]; + wire [aw-1:0] mem_adr; + assign mem_adr = (i_wen==1'b1) ? i_waddr : + i_raddr; + + // Second port wishbone + wire [31:0] wb_adr; + wire [31:0] wb_dat; + reg [31:0] wb_rdt; + wire [3:0] wb_sel; + wire wb_cyc; + wire wb_we; + wire wb_stb; + reg wb_ack; + reg wb_req_d; + wire cmd_reset; + // Driven by JTAG + jtag_wb_bridge #( + .chain(1), + .byte_aligned(1) + ) jtag_wb ( + .i_clk(i_clk), + .i_rst(i_rst), + + .o_wb_adr(wb_adr), + .o_wb_dat(wb_dat), + .o_wb_sel(wb_sel), + .o_wb_we(wb_we), + .o_wb_cyc(wb_cyc), + .o_wb_stb(wb_stb), + .i_wb_rdt(wb_rdt), + .i_wb_ack(wb_ack), + .o_cmd_reset(cmd_reset) + ); + + assign o_core_reset = cmd_reset; + + // Read/Write + always @(posedge i_clk) begin + if (i_rst) begin + wb_req_d <= 1'b0; + wb_ack <= 1'b0; + wb_rdt <= 32'h00000000; + o_rdata <= 32'h00000000; + end else begin + if (i_wen) + mem[mem_adr] <= i_wdata; + o_rdata <= mem[mem_adr]; + + wb_req_d <= wb_stb && wb_cyc; + wb_ack <= wb_req_d; + if (wb_we && wb_stb && wb_cyc) + mem[wb_adr[aw-1:0]] <= wb_dat[7:0]; + wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]}; + end + end + + // Preload memory + integer i; + initial begin + if(sim==1'b1) begin + for (i = 0; i < depth; i = i + 1) + mem[i] = 8'h00; + end + if(|memfile) begin + $display("Preloading %m from %s", memfile); + $readmemh(memfile, mem); + end + end + + + +endmodule diff --git a/rtl/core/soclet.v b/rtl/core/soclet.v deleted file mode 100644 index 7c1ecc5..0000000 --- a/rtl/core/soclet.v +++ /dev/null @@ -1,77 +0,0 @@ -`timescale 1ns/1ps - -module soclet #( - parameter memfile = "", - parameter memsize = 8192, - parameter sim = 1'b0 -)( - input wire i_clk, - input wire i_rst, - - input wire [31:0] i_GPI_A, - input wire [31:0] i_GPI_B, - input wire [31:0] i_GPI_C, - input wire [31:0] i_GPI_D, - - output wire [31:0] o_GPO_A, - output wire [31:0] o_GPO_B, - output wire [31:0] o_GPO_C, - output wire [31:0] o_GPO_D -); - - wire [31:0] wb_adr; - wire [31:0] wb_dat; - wire [31:0] wb_rdt; - wire [3:0] wb_sel; - wire wb_we; - wire wb_stb; - wire wb_ack; - - wire [4*32-1:0] GPO; - wire [4*32-1:0] GPI; - assign o_GPO_A = GPO[32*1-1:32*0]; - assign o_GPO_B = GPO[32*2-1:32*1]; - assign o_GPO_C = GPO[32*3-1:32*2]; - assign o_GPO_D = GPO[32*4-1:32*3]; - assign GPI[32*1-1:32*0] = i_GPI_A; - assign GPI[32*2-1:32*1] = i_GPI_B; - assign GPI[32*3-1:32*2] = i_GPI_C; - assign GPI[32*4-1:32*3] = i_GPI_D; - - serving #( - .memfile(memfile), - .memsize(memsize), - .sim(sim), - .RESET_STRATEGY("MINI"), - .WITH_CSR(1) - ) serv ( - .i_clk(i_clk), - .i_rst(i_rst), - .i_timer_irq(1'b0), - .i_wb_rdt(wb_rdt), - .i_wb_ack(wb_ack), - .o_wb_adr(wb_adr), - .o_wb_dat(wb_dat), - .o_wb_sel(wb_sel), - .o_wb_we(wb_we), - .o_wb_stb(wb_stb) - ); - - wb_gpio_banks #( - .BASE_ADDR(32'h40000000), - .NUM_BANKS(4) - ) gpio ( - .i_wb_clk(i_clk), - .i_wb_rst(i_rst), - .i_wb_dat(wb_dat), - .i_wb_adr(wb_adr), - .i_wb_we(wb_we), - .i_wb_stb(wb_stb), - .i_wb_sel(wb_sel), - .i_gpio(GPI), - .o_wb_rdt(wb_rdt), - .o_wb_ack(wb_ack), - .o_gpio(GPO) - ); - -endmodule \ No newline at end of file diff --git a/rtl/toplevel/top_generic.v b/rtl/toplevel/top_generic.v index a3adde1..a2c3c6e 100644 --- a/rtl/toplevel/top_generic.v +++ b/rtl/toplevel/top_generic.v @@ -1,6 +1,8 @@ `timescale 1ns/1ps -module top_generic( +module top_generic #( + parameter sim = 0 +)( input wire aclk, input wire aresetn, @@ -11,9 +13,6 @@ module top_generic( output wire[7:0] LED ); `include "conv.vh" - assign led_green = 1'b0; - assign led_red = 1'b0; - assign LED = 8'h00; // Clocking wire clk_100; @@ -29,8 +28,11 @@ module top_generic( wire [31:0] GPIO_C; wire [31:0] GPIO_D; + wire test; + mcu #( - .memfile("../sw/sweep/sweep.hex") + .memfile("../sw/sweep/sweep.hex"), + .sim(sim) ) mcu ( .i_clk(clk_15), .i_rst(!aresetn), @@ -41,7 +43,8 @@ module top_generic( .o_GPO_A(GPIO_A), .o_GPO_B(GPIO_B), .o_GPO_C(GPIO_C), - .o_GPO_D(GPIO_D) + .o_GPO_D(GPIO_D), + .o_test(test) ); @@ -64,4 +67,8 @@ module top_generic( dac_code <= q15_to_uq16(sin_q15) >> 10; end assign r2r = dac_code; + + assign LED = GPIO_B[7:0]; + assign led_green = GPIO_C[0]; + assign led_red = test; endmodule diff --git a/rtl/wb/wb_mux.v b/rtl/wb/wb_mux.v new file mode 100644 index 0000000..76ae0f0 --- /dev/null +++ b/rtl/wb/wb_mux.v @@ -0,0 +1,145 @@ +/* wb_mux. Part of wb_intercon + * + * ISC License + * + * Copyright (C) 2013-2019 Olof Kindgren + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + Wishbone multiplexer, burst-compatible + + Simple mux with an arbitrary number of slaves. + + The parameters MATCH_ADDR and MATCH_MASK are flattened arrays + aw*NUM_SLAVES sized arrays that are used to calculate the + active slave. slave i is selected when + (wb_adr_i & MATCH_MASK[(i+1)*aw-1:i*aw] is equal to + MATCH_ADDR[(i+1)*aw-1:i*aw] + If several regions are overlapping, the slave with the lowest + index is selected. This can be used to have fallback + functionality in the last slave, in case no other slave was + selected. + + If no match is found, the wishbone transaction will stall and + an external watchdog is required to abort the transaction + + Todo: + Registered master/slave connections + Rewrite with System Verilog 2D arrays when tools support them +*/ +`include "../util/clog2.vh" + +module wb_mux + #(parameter dw = 32, // Data width + parameter aw = 32, // Address width + parameter num_devices = 2, // Number of devices + parameter num_slaves = num_devices, // Number of devices (deprecated) + parameter [num_slaves*aw-1:0] MATCH_ADDR = 0, + parameter [num_slaves*aw-1:0] MATCH_MASK = 0) + + ( + input wire wb_clk_i, + input wire wb_rst_i, + + // Master Interface + input wire [aw-1:0] wbm_adr_i, + input wire [dw-1:0] wbm_dat_i, + input wire [3:0] wbm_sel_i, + input wire wbm_we_i, + input wire wbm_cyc_i, + input wire wbm_stb_i, + input wire [2:0] wbm_cti_i, + input wire [1:0] wbm_bte_i, + output wire [dw-1:0] wbm_dat_o, + output wire wbm_ack_o, + output wire wbm_err_o, + output wire wbm_rty_o, + // Wishbone Slave interface + output wire [num_slaves*aw-1:0] wbs_adr_o, + output wire [num_slaves*dw-1:0] wbs_dat_o, + output wire [num_slaves*4-1:0] wbs_sel_o, + output wire [num_slaves-1:0] wbs_we_o, + output wire [num_slaves-1:0] wbs_cyc_o, + output wire [num_slaves-1:0] wbs_stb_o, + output wire [num_slaves*3-1:0] wbs_cti_o, + output wire [num_slaves*2-1:0] wbs_bte_o, + input wire [num_slaves*dw-1:0] wbs_dat_i, + input wire [num_slaves-1:0] wbs_ack_i, + input wire [num_slaves-1:0] wbs_err_i, + input wire [num_slaves-1:0] wbs_rty_i); + +/////////////////////////////////////////////////////////////////////////////// +// Master/slave connection +/////////////////////////////////////////////////////////////////////////////// + + //Use parameter instead of localparam to work around a bug in Xilinx ISE + parameter slave_sel_bits = num_slaves > 1 ? `CLOG2(num_slaves) : 1; + + reg wbm_err; + wire [slave_sel_bits-1:0] slave_sel; + wire [num_slaves-1:0] match; + + genvar idx; + + generate + for(idx=0; idx= 0; i=i-1) begin + if (in[i]) +/* verilator lint_off WIDTH */ + ff1 = i; +/* verilator lint_on WIDTH */ + end + end + endfunction + + assign slave_sel = ff1(match); + + always @(posedge wb_clk_i) + wbm_err <= wbm_cyc_i & !(|match); + + assign wbs_adr_o = {num_slaves{wbm_adr_i}}; + assign wbs_dat_o = {num_slaves{wbm_dat_i}}; + assign wbs_sel_o = {num_slaves{wbm_sel_i}}; + assign wbs_we_o = {num_slaves{wbm_we_i}}; +/* verilator lint_off WIDTH */ + + // Expand master CYC to slave bus width before shifting to one-hot select. + // Shifting a 1-bit signal would otherwise zero out all but slave 0. + assign wbs_cyc_o = match & ({num_slaves{wbm_cyc_i}} << slave_sel); +/* verilator lint_on WIDTH */ + assign wbs_stb_o = {num_slaves{wbm_stb_i}}; + + assign wbs_cti_o = {num_slaves{wbm_cti_i}}; + assign wbs_bte_o = {num_slaves{wbm_bte_i}}; + + assign wbm_dat_o = wbs_dat_i[slave_sel*dw+:dw]; + assign wbm_ack_o = wbs_ack_i[slave_sel]; + assign wbm_err_o = wbs_err_i[slave_sel] | wbm_err; + assign wbm_rty_o = wbs_rty_i[slave_sel]; + +endmodule diff --git a/rtl/wb/wb_timer.v b/rtl/wb/wb_timer.v new file mode 100644 index 0000000..ac14586 --- /dev/null +++ b/rtl/wb/wb_timer.v @@ -0,0 +1,74 @@ +`timescale 1ns/1ps + +module wb_countdown_timer #( + parameter WIDTH = 32, // counter width (<=32 makes bus mapping easy) + parameter DIVIDER = 0 // optional prescaler: tick every 2^DIVIDER cycles +)( + input wire i_clk, + input wire i_rst, + output reg o_irq, + + input wire [31:0] i_wb_dat, + output reg [31:0] o_wb_dat, + input wire i_wb_we, + input wire i_wb_cyc, + input wire i_wb_stb, + output wire o_wb_ack +); + + // One-cycle acknowledge on any valid WB access + // (classic, zero-wait-state peripheral) + assign o_wb_ack = i_wb_cyc & i_wb_stb; + + // Internal countdown and prescaler + reg [WIDTH-1:0] counter; + reg [DIVIDER:0] presc; // enough bits to count up to 2^DIVIDER-1 + wire tick = (DIVIDER == 0) ? 1'b1 : (presc[DIVIDER] == 1'b1); + + // Readback: expose the current counter value + always @(*) begin + o_wb_dat = 32'd0; + o_wb_dat[WIDTH-1:0] = counter; + end + + // Main logic + always @(posedge i_clk) begin + if (i_rst) begin + counter <= {WIDTH{1'b0}}; + presc <= { (DIVIDER+1){1'b0} }; + o_irq <= 1'b0; + end else begin + // Default prescaler behavior + if (DIVIDER != 0) begin + if (counter != 0 && !o_irq) + presc <= presc + 1'b1; + else + presc <= { (DIVIDER+1){1'b0} }; + end + + // Wishbone write: load counter and clear IRQ + if (o_wb_ack && i_wb_we) begin + counter <= i_wb_dat[WIDTH-1:0]; + o_irq <= 1'b0; + + // reset prescaler on (re)start or stop + presc <= { (DIVIDER+1){1'b0} }; + + end else begin + // Countdown when running (counter>0), not already IRQ'd + if (!o_irq && counter != 0) begin + if (tick) begin + if (counter == 1) begin + counter <= {WIDTH{1'b0}}; + o_irq <= 1'b1; // sticky until next write + presc <= { (DIVIDER+1){1'b0} }; + end else begin + counter <= counter - 1'b1; + end + end + end + end + end + end + +endmodule \ No newline at end of file diff --git a/sim/tb/tb_top_generic.v b/sim/tb/tb_top_generic.v new file mode 100644 index 0000000..f7261ff --- /dev/null +++ b/sim/tb/tb_top_generic.v @@ -0,0 +1,45 @@ +`timescale 1ns/1ps + +module tb_top_generic(); + reg aclk; + reg aresetn; + + wire led_green; + wire led_red; + wire [5:0] r2r; + wire [7:0] LED; + + // 100 MHz board input clock + initial aclk = 1'b0; + always #5 aclk = ~aclk; + + // Hold reset low, then release + initial begin + aresetn = 1'b0; + #200; + aresetn = 1'b1; + end + + top_generic #( + .sim(1) + ) dut ( + .aclk(aclk), + .aresetn(aresetn), + .led_green(led_green), + .led_red(led_red), + .r2r(r2r), + .LED(LED) + ); + + // Ensure firmware path resolves from repository root when simulating. + defparam dut.mcu.memfile = "sw/sweep/sweep.hex"; + + initial begin + $dumpfile("out.vcd"); + $dumpvars(0, tb_top_generic); + + // Let firmware run for a while. + #5_000_000; + $finish; + end +endmodule diff --git a/sim/tb/tb_wb_timer.v b/sim/tb/tb_wb_timer.v new file mode 100644 index 0000000..4afcd8d --- /dev/null +++ b/sim/tb/tb_wb_timer.v @@ -0,0 +1,157 @@ +`timescale 1ns/1ps + +module tb_wb_timer(); + localparam integer CLK_HALF_NS = 5; + + reg clk; + reg rst; + + reg [31:0] i_wb_dat; + wire [31:0] o_wb_dat; + reg i_wb_we; + reg i_wb_cyc; + reg i_wb_stb; + wire o_wb_ack; + wire o_irq; + + integer errors; + + wb_countdown_timer #( + .WIDTH(8), + .DIVIDER(0) + ) dut ( + .i_clk (clk), + .i_rst (rst), + .o_irq (o_irq), + .i_wb_dat(i_wb_dat), + .o_wb_dat(o_wb_dat), + .i_wb_we (i_wb_we), + .i_wb_cyc(i_wb_cyc), + .i_wb_stb(i_wb_stb), + .o_wb_ack(o_wb_ack) + ); + + always #CLK_HALF_NS clk = ~clk; + + task wb_write; + input [31:0] data; + begin + @(negedge clk); + i_wb_dat = data; + i_wb_we = 1'b1; + i_wb_cyc = 1'b1; + i_wb_stb = 1'b1; + + #1; + if (!o_wb_ack) begin + $display("FAIL: WB write ACK not asserted when cyc/stb high"); + errors = errors + 1; + end + + @(negedge clk); + i_wb_we = 1'b0; + i_wb_cyc = 1'b0; + i_wb_stb = 1'b0; + end + endtask + + task wb_read_check; + input [31:0] expected; + begin + @(negedge clk); + i_wb_we = 1'b0; + i_wb_cyc = 1'b1; + i_wb_stb = 1'b1; + + #1; + if (!o_wb_ack) begin + $display("FAIL: WB read ACK not asserted when cyc/stb high"); + errors = errors + 1; + end + + if (o_wb_dat !== expected) begin + $display("FAIL: WB read mismatch. got=0x%08h expected=0x%08h", o_wb_dat, expected); + errors = errors + 1; + end + + @(negedge clk); + i_wb_cyc = 1'b0; + i_wb_stb = 1'b0; + end + endtask + + task wait_irq_with_timeout; + input integer max_cycles; + integer k; + begin + k = 0; + while ((o_irq !== 1'b1) && (k < max_cycles)) begin + @(posedge clk); + k = k + 1; + end + + if (o_irq !== 1'b1) begin + $display("FAIL: IRQ did not assert within %0d cycles", max_cycles); + errors = errors + 1; + end + end + endtask + + initial begin + $dumpfile("out.vcd"); + $dumpvars; + + clk = 1'b0; + rst = 1'b1; + i_wb_dat = 32'd0; + i_wb_we = 1'b0; + i_wb_cyc = 1'b0; + i_wb_stb = 1'b0; + errors = 0; + + $display("---------------------------------------"); + $display("Testbench: wb_countdown_timer"); + $display("---------------------------------------"); + + repeat (3) @(posedge clk); + rst = 1'b0; + repeat (1) @(posedge clk); + + if (o_irq !== 1'b0) begin + $display("FAIL: IRQ should be low after reset"); + errors = errors + 1; + end + wb_read_check(32'd0); + + // Load timer and check readback at a deterministic point + wb_write(32'd5); + @(posedge clk); + wb_read_check(32'd4); + + // Wait until expiration, then verify sticky IRQ behavior + wait_irq_with_timeout(6); + wb_read_check(32'd0); + + repeat (2) @(posedge clk); + if (o_irq !== 1'b1) begin + $display("FAIL: IRQ should remain sticky until next write"); + errors = errors + 1; + end + + // A new write must clear IRQ and restart the counter + wb_write(32'd2); + if (o_irq !== 1'b0) begin + $display("FAIL: IRQ should clear on write"); + errors = errors + 1; + end + + wait_irq_with_timeout(3); + + if (errors == 0) + $display("PASS: wb_timer basic functionality verified"); + else + $display("FAIL: wb_timer testbench found %0d error(s)", errors); + + $finish; + end +endmodule diff --git a/sw/sweep/link.ld b/sw/sweep/link.ld index 483b1de..4f0c0db 100644 --- a/sw/sweep/link.ld +++ b/sw/sweep/link.ld @@ -3,7 +3,7 @@ ENTRY(_start) MEMORY { - RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064 + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8192 } SECTIONS @@ -29,5 +29,5 @@ SECTIONS } > RAM . = ALIGN(4); - __stack_top = ORIGIN(RAM) + LENGTH(RAM); + __stack_top = ORIGIN(RAM) + LENGTH(RAM) - 256; } diff --git a/sw/sweep/sweep.c b/sw/sweep/sweep.c index 98910c7..5d4e060 100644 --- a/sw/sweep/sweep.c +++ b/sw/sweep/sweep.c @@ -8,7 +8,7 @@ void main(){ for(;;){ for(int i=1000; i<10000; i++){ *R_FREQ = i; - for(int j=0; j<100; j++) asm volatile("nop"); + // for(int j=0; j<100; j++) asm volatile("nop"); } } } \ No newline at end of file