diff --git a/example_soc/fpga/fpga_icebreaker.v b/example_soc/fpga/fpga_icebreaker.v index c5493a7..dfb548f 100644 --- a/example_soc/fpga/fpga_icebreaker.v +++ b/example_soc/fpga/fpga_icebreaker.v @@ -62,6 +62,7 @@ activity_led #( ); example_soc #( + .CLK_MHZ (12), .EXTENSION_A (1), .EXTENSION_C (0), .EXTENSION_M (1), @@ -72,6 +73,7 @@ example_soc #( .EXTENSION_ZBKB (0), .EXTENSION_ZIFENCEI (0), .EXTENSION_XH3BEXTM (0), + .EXTENSION_XH3PMPM (0), .EXTENSION_XH3POWER (0), .CSR_COUNTER (0), .U_MODE (0), diff --git a/example_soc/fpga/fpga_ulx3s.v b/example_soc/fpga/fpga_ulx3s.v index dd3ecce..2873182 100644 --- a/example_soc/fpga/fpga_ulx3s.v +++ b/example_soc/fpga/fpga_ulx3s.v @@ -17,7 +17,7 @@ wire clk_sys; wire pll_sys_locked; wire rst_n_sys; -pll_25_40 pll_sys ( +pll_25_50 pll_sys ( .clkin (clk_osc), .clkout0 (clk_sys), .locked (pll_sys_locked) @@ -32,19 +32,29 @@ fpga_reset #( ); example_soc #( - .DTM_TYPE ("ECP5"), - .SRAM_DEPTH (1 << 15), + .DTM_TYPE ("ECP5"), + .SRAM_DEPTH (1 << 15), + .CLK_MHZ (50), - .EXTENSION_M (1), - .EXTENSION_A (1), - .EXTENSION_C (0), - .EXTENSION_ZBA (0), - .EXTENSION_ZBB (0), - .EXTENSION_ZBC (0), - .EXTENSION_ZBS (0), - .CSR_COUNTER (0), - .MUL_FAST (1), - .MULDIV_UNROLL (1) + .EXTENSION_M (1), + .EXTENSION_A (1), + .EXTENSION_C (0), + .EXTENSION_ZBA (0), + .EXTENSION_ZBB (0), + .EXTENSION_ZBC (0), + .EXTENSION_ZBS (0), + .EXTENSION_ZBKB (0), + .EXTENSION_ZIFENCEI (1), + .EXTENSION_XH3BEXTM (0), + .EXTENSION_XH3PMPM (0), + .EXTENSION_XH3POWER (0), + .CSR_COUNTER (1), + .MUL_FAST (1), + .MUL_FASTER (0), + .MULH_FAST (0), + .MULDIV_UNROLL (1), + .FAST_BRANCHCMP (1), + .BRANCH_PREDICTOR (1) ) soc_u ( .clk (clk_sys), .rst_n (rst_n_sys), diff --git a/example_soc/soc/example_soc.v b/example_soc/soc/example_soc.v index c956479..2f28e97 100644 --- a/example_soc/soc/example_soc.v +++ b/example_soc/soc/example_soc.v @@ -9,8 +9,9 @@ `default_nettype none module example_soc #( - parameter DTM_TYPE = "JTAG", // can be "JTAG" or "ECP5" - parameter SRAM_DEPTH = 1 << 15, // default 32 kwords -> 128 kB + parameter DTM_TYPE = "JTAG", // Can be "JTAG" or "ECP5" + parameter SRAM_DEPTH = 1 << 15, // Default 32 kwords -> 128 kB + parameter CLK_MHZ = 12, // For timer timebase `include "hazard3_config.vh" ) ( @@ -262,6 +263,8 @@ hazard3_cpu_1port #( .EXTENSION_ZBKB (EXTENSION_ZBKB), .EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI), .EXTENSION_XH3BEXTM (EXTENSION_XH3BEXTM), + .EXTENSION_XH3IRQ (EXTENSION_XH3IRQ), + .EXTENSION_XH3PMPM (EXTENSION_XH3PMPM), .EXTENSION_XH3POWER (EXTENSION_XH3POWER), .CSR_COUNTER (CSR_COUNTER), .U_MODE (U_MODE), @@ -538,30 +541,43 @@ uart_mini uart_u ( .dreq (/* unused */) ); -// hazard3_riscv_timer timer_u ( -// .clk (clk), -// .rst_n (rst_n), +// Microsecond timebase for timer -// .psel (timer_psel), -// .penable (timer_penable), -// .pwrite (timer_pwrite), -// .paddr (timer_paddr), -// .pwdata (timer_pwdata), -// .prdata (timer_prdata), -// .pready (timer_pready), -// .pslverr (timer_pslverr), +reg [$clog2(CLK_MHZ)-1:0] timer_tick_ctr; +reg timer_tick; -// .dbg_halt (&hart_halted), +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + timer_tick_ctr <= {$clog2(CLK_MHZ){1'b0}}; + timer_tick <= 1'b0; + end else begin + if (|timer_tick_ctr) begin + timer_tick_ctr <= timer_tick_ctr - 1'b1; + end else begin + timer_tick_ctr <= CLK_MHZ - 1; + end + timer_tick <= ~|timer_tick_ctr; + end +end -// // Tie high for 64-cycle timebase: -// .tick (1'b1), +hazard3_riscv_timer timer_u ( + .clk (clk), + .rst_n (rst_n), -// .timer_irq (timer_irq) -// ); + .psel (timer_psel), + .penable (timer_penable), + .pwrite (timer_pwrite), + .paddr (timer_paddr), + .pwdata (timer_pwdata), + .prdata (timer_prdata), + .pready (timer_pready), + .pslverr (timer_pslverr), -assign timer_pslverr = 1'b0; -assign timer_pready = 1'b1; -assign timer_prdata = 32'h0; -assign timer_irq = 1'b0; + .dbg_halt (&hart_halted), + + .tick (timer_tick), + + .timer_irq (timer_irq) +); endmodule diff --git a/example_soc/soc/peri/hazard3_riscv_timer.f b/example_soc/soc/peri/hazard3_riscv_timer.f new file mode 100644 index 0000000..3792bc5 --- /dev/null +++ b/example_soc/soc/peri/hazard3_riscv_timer.f @@ -0,0 +1,2 @@ +file hazard3_riscv_timer.v +file ../../../hdl/debug/cdc/hazard3_sync_1bit.v diff --git a/example_soc/soc/peri/hazard3_riscv_timer.v b/example_soc/soc/peri/hazard3_riscv_timer.v new file mode 100644 index 0000000..31218f6 --- /dev/null +++ b/example_soc/soc/peri/hazard3_riscv_timer.v @@ -0,0 +1,142 @@ +/*****************************************************************************\ +| Copyright (C) 2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +// Basic implementation of standard 64-bit RISC-V timer with 32-bit APB. + +// TICK_IS_NRZ = 1: tick is an NRZ signal that is asynchronous to clk. +// TICK_IS_NRZ = 0: tick is a level-sensitive signal that is synchronous to clk. + +module hazard3_riscv_timer #( + parameter TICK_IS_NRZ = 0 +) ( + input wire clk, + input wire rst_n, + + input wire [15:0] paddr, + input wire psel, + input wire penable, + input wire pwrite, + input wire [31:0] pwdata, + output reg [31:0] prdata, + output wire pready, + output wire pslverr, + + input wire dbg_halt, + input wire tick, + + output reg timer_irq +); + +localparam ADDR_CTRL = 16'h0000; +localparam ADDR_MTIME = 16'h0008; +localparam ADDR_MTIMEH = 16'h000c; +localparam ADDR_MTIMECMP = 16'h0010; +localparam ADDR_MTIMECMPH = 16'h0014; + +// ---------------------------------------------------------------------------- +// Timer tick logic + +wire tick_event; + +generate +if (TICK_IS_NRZ) begin: edge_detect + + wire tick_nrz_sync; + + hazard3_sync_1bit tick_sync_u ( + .clk (clk), + .rst_n (rst_n), + .i (tick_nrz), + .o (tick_nrz_sync) + ); + + reg tick_nrz_prev; + always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + tick_nrz_prev <= 1'b0; + end else begin + tick_nrz_prev <= tick_nrz_sync; + end + end + + assign tick_event = tick_nrz_sync ^ tick_nrz_sync_prev; + +end else begin: no_edge_detect + + assign tick_event = tick; + +end +endgenerate + +reg ctrl_en; +wire tick_now = tick_event && ctrl_en && !dbg_halt; + +// ---------------------------------------------------------------------------- +// Counter registers + +wire bus_write = pwrite && psel && penable; +wire bus_read = !pwrite && psel && penable; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + ctrl_en <= 1'b1; + end else if (bus_write && paddr == ADDR_CTRL) begin + ctrl_en <= pwdata[0]; + end +end + +reg [63:0] mtime; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mtime <= 64'h0; + end else begin + if (tick_now) + mtime <= mtime + 1'b1; + if (bus_write && paddr == ADDR_MTIME) + mtime[31:0] <= pwdata; + if (bus_write && paddr == ADDR_MTIMEH) + mtime[63:32] <= pwdata; + end +end + +// mtimecmp is stored inverted for minor LUT savings on iCE40 +reg [63:0] mtimecmp; +wire [64:0] cmp_diff = {1'b0, mtime} + {1'b0, mtimecmp} + 65'd1; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mtimecmp <= 64'h0; + timer_irq <= 1'b0; + end else begin + if (bus_write && paddr == ADDR_MTIMECMP) + mtimecmp[31:0] <= ~pwdata; + if (bus_write && paddr == ADDR_MTIMECMPH) + mtimecmp[63:32] <= ~pwdata; + timer_irq <= cmp_diff[64]; + end +end + +always @ (*) begin + case (paddr) + ADDR_CTRL: prdata = {31'h0, ctrl_en}; + ADDR_MTIME: prdata = mtime[31:0]; + ADDR_MTIMEH: prdata = mtime[63:32]; + ADDR_MTIMECMP: prdata = ~mtimecmp[31:0]; + ADDR_MTIMECMPH: prdata = ~mtimecmp[63:32]; + default: prdata = 32'h0; + endcase +end + +assign pready = 1'b1; +assign pslverr = 1'b0; + +endmodule + +`ifndef YOSYS +`default_nettype none +`endif diff --git a/example_soc/soc/soc.f b/example_soc/soc/soc.f index 8c4ab70..89f0378 100644 --- a/example_soc/soc/soc.f +++ b/example_soc/soc/soc.f @@ -8,6 +8,10 @@ list $HDL/hazard3.f list $HDL/debug/dtm/hazard3_jtag_dtm.f list $HDL/debug/dm/hazard3_dm.f +# RISC-V timer + +list peri/hazard3_riscv_timer.f + # Generic SoC components from libfpga file ../libfpga/common/reset_sync.v