Browse Source

picorv32: export user_context and addr[1:0] to outside bus

Make the current user_context as well as the lower two bits of the
address available to the outside bus. The latter is useful for dealing
with situations where read side effects are necessary.

Fix a bug in the sdram port code triggered by exporting addr[1:0].

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
H. Peter Anvin 2 years ago
parent
commit
3672b35b21
2 changed files with 115 additions and 103 deletions
  1. 114 102
      fpga/picorv32.v
  2. 1 1
      fpga/sdram.sv

+ 114 - 102
fpga/picorv32.v

@@ -2,6 +2,7 @@
  *  PicoRV32 -- A Small RISC-V (RV32I) Processor Core
  *
  *  Copyright (C) 2015  Clifford Wolf <clifford@clifford.at>
+ *  Heavily modified (in incompatible ways!) by H. Peter Anvin <hpa@zytor.com>
  *
  *  Permission to use, copy, modify, and/or distribute this software for any
  *  purpose with or without fee is hereby granted, provided that the above
@@ -20,34 +21,41 @@
  *  - retirq opcode changed to mret.
  *  - qregs replaced with a full register bank switch. In general,
  *    non-power-of-two register files don't save anything, especially in
- *    FPGAs.
+ *    FPGAs. The interrupt mask is saved in x27/s11.
  *  - getq and setq replaced with new instructions addqxi and addxqi
  *    for cross-bank register accesses if needed,
  *    taking immediate as additive argument.
  *    e.g. for stack setup (addqxi sp,sp,frame_size).
+ *  - On FPGAs SRAM can be (and pretty much universally is)
+ *    initialized to all zero in hardware. Implement x0 = 0 by
+ *    disabling the write enable for this register; this improves
+ *    timing by avoiding MUXes in the data and address paths.
  *  - PROGADDR_RESET and PROGADDR_IRQ changed to ports (allows external
  *    implementation of vectorized interrupts or fallback reset.)
  *  - maskirq, waitirq and timer require func3 == 3'b000.
- *  - add two masks to waitirq: an AND mask and an OR mask.
+ *  - Add two masks to waitirq: an AND mask and an OR mask.
  *    waitirq exists if either all interrupts in the AND
  *    mask are pending or any interrupt in the OR mask is pending.
  *    Note that waitirq with an AND mask of zero will exit immediately;
- *    this can be used to poll the status of interrupts (masked and unmasked.)
- *  - multiple user (non-interrupt) register banks (tasks) now supported;
+ *    this can be used to poll the status of interrupts (masked and unmasked)
+ *    without sending EOI (see pollirq below for variant with EOI.)
+ *  - Multiple user (non-interrupt) register banks (tasks) now supported;
  *    these are set via a custom user_context CSR (0x7f0). They are numbered
  *    starting with 1; 0 is reserved for the IRQ context. After reset,
  *    this register is set to the maximum supported user context number.
  *    Writing this register also causes a transition to the IRQ context,
  *    so the context switch can be processed atomically.
- *  - the interrupt return address moved the mepc CSR, to make it
+ *  - The interrupt return address moved the mepc CSR, to make it
  *    globally available at interrupt time. This simplifies context switching.
- *  - implement the ctz instruction from the Zbb extension to improve
+ *    Writing mepc from user context switches to IRQ context.
+ *  - Implement the ctz instruction from the Zbb extension to improve
  *    interrupt latency by speeding up the dispatch substantially.
- *  - new pollirq instruction: returns a mask of pending unmasked
+ *  - New pollirq instruction: returns a mask of pending unmasked
  *    interrupts AND ~rs1 OR rs2. EOIs pending unmasked interrupts AND ~rs1.
  *    This is intended to avoid priority inversion in the IRQ dispatch.
- *  - separately parameterize the width of the cycle and instruction counters;
+ *  - Separately parameterize the width of the cycle and instruction counters;
  *    they can be independently set to any value from 0 to 64 bits.
+ *  - The user context number (user_context CSR) is exported to a port.
  */
 
 /* verilator lint_off WIDTH */
@@ -106,105 +114,111 @@ endfunction // do_ctz
 module picorv32 #(
 	parameter integer COUNTER_CYCLE_WIDTH = 64,
 	parameter integer COUNTER_INSTR_WIDTH = 64,
-	parameter [ 0:0] ENABLE_REGS_16_31 = 1,
-	parameter [ 0:0] ENABLE_REGS_DUALPORT = 1,
-	parameter [ 0:0] LATCHED_MEM_RDATA = 0,
-	parameter [ 0:0] TWO_STAGE_SHIFT = 1,
-	parameter [ 0:0] BARREL_SHIFTER = 0,
-	parameter [ 0:0] TWO_CYCLE_COMPARE = 0,
-	parameter [ 0:0] TWO_CYCLE_ALU = 0,
-	parameter [ 0:0] COMPRESSED_ISA = 0,
-	parameter [ 0:0] CATCH_MISALIGN = 1,
-	parameter [ 0:0] CATCH_ILLINSN = 1,
-	parameter [ 0:0] ENABLE_PCPI = 0,
-	parameter [ 0:0] ENABLE_MUL = 0,
-	parameter [ 0:0] ENABLE_FAST_MUL = 0,
-	parameter [ 0:0] ENABLE_DIV = 0,
-	parameter [ 0:0] ENABLE_IRQ = 0,
-	parameter [ 0:0] ENABLE_IRQ_TIMER = 1,
-	parameter [ 0:0] ENABLE_TRACE = 0,
-	parameter [ 0:0] REGS_INIT_ZERO = 0,
-	parameter [31:0] MASKED_IRQ = 32'h 0000_0000,
-	parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff,
-	parameter [31:0] STACKADDR = 32'h ffff_ffff,
-	parameter [ 4:0] MASK_IRQ_REG = ENABLE_IRQ_QREGS ? 27 : 4,
-	parameter	 USER_CONTEXTS = 1,
-	parameter [ 0:0] ENABLE_IRQ_QREGS = USER_CONTEXTS > 0
+	parameter [ 0:0]  ENABLE_REGS_16_31 = 1,
+	parameter [ 0:0]  ENABLE_REGS_DUALPORT = 1,
+	parameter [ 0:0]  LATCHED_MEM_RDATA = 0,
+	parameter [ 0:0]  TWO_STAGE_SHIFT = 1,
+	parameter [ 0:0]  BARREL_SHIFTER = 0,
+	parameter [ 0:0]  TWO_CYCLE_COMPARE = 0,
+	parameter [ 0:0]  TWO_CYCLE_ALU = 0,
+	parameter [ 0:0]  COMPRESSED_ISA = 0,
+	parameter [ 0:0]  CATCH_MISALIGN = 1,
+	parameter [ 0:0]  CATCH_ILLINSN = 1,
+	parameter [ 0:0]  ENABLE_PCPI = 0,
+	parameter [ 0:0]  ENABLE_MUL = 0,
+	parameter [ 0:0]  ENABLE_FAST_MUL = 0,
+	parameter [ 0:0]  ENABLE_DIV = 0,
+	parameter [ 0:0]  ENABLE_IRQ = 0,
+	parameter [ 0:0]  ENABLE_IRQ_TIMER = 1,
+	parameter [ 0:0]  ENABLE_TRACE = 0,
+	parameter [ 0:0]  REGS_INIT_ZERO = 0,
+	parameter [31:0]  MASKED_IRQ = 32'h 0000_0000,
+	parameter [31:0]  LATCHED_IRQ = 32'h ffff_ffff,
+	parameter [31:0]  STACKADDR = 32'h ffff_ffff,
+	parameter [ 4:0]  MASK_IRQ_REG = ENABLE_IRQ_QREGS ? 27 : 4,
+	parameter integer USER_CONTEXTS = 1,
+	parameter [ 0:0]  ENABLE_IRQ_QREGS = USER_CONTEXTS > 0,
+
+	parameter integer context_bits = $clog2(USER_CONTEXTS + 1),
+	parameter integer context_max_bit = context_bits ? context_bits-1 : 0
 ) (
-	input		  clk, resetn,
-	input		  halt,
-	output reg	  trap,
+	input			       clk, resetn,
+	input			       halt,
+	output reg		       trap,
 
-	input [31:0]	  progaddr_reset,
-	input [31:0]	  progaddr_irq,
+	input [31:0]		       progaddr_reset,
+	input [31:0]		       progaddr_irq,
 
-	output reg	  mem_valid,
-	output reg	  mem_instr,
-	input		  mem_ready,
+	output reg		       mem_valid,
+	output reg		       mem_instr,
+	input			       mem_ready,
 
-	output reg [31:0] mem_addr,
-	output reg [31:0] mem_wdata,
-	output reg [ 3:0] mem_wstrb,
-	input [31:0]	  mem_rdata,
+	output reg [31:0]	       mem_addr,
+	output reg [31:0]	       mem_wdata,
+	output reg [ 3:0]	       mem_wstrb,
+	input [31:0]		       mem_rdata,
 
 	// Look-Ahead Interface
-	output		  mem_la_read,
-	output		  mem_la_write,
-	output [31:0]	  mem_la_addr,
-	output reg [31:0] mem_la_wdata,
-	output reg [ 3:0] mem_la_wstrb,
+	output			       mem_la_read,
+	output			       mem_la_write,
+	output [31:0]		       mem_la_addr,
+	output reg [31:0]	       mem_la_wdata,
+	output reg [ 3:0]	       mem_la_wstrb,
 
 	// Pico Co-Processor Interface (PCPI)
-	output reg	  pcpi_valid,
-	output reg [31:0] pcpi_insn,
-	output [31:0]	  pcpi_rs1,
-	output [31:0]	  pcpi_rs2,
-	input		  pcpi_wr,
-	input [31:0]	  pcpi_rd,
-	input		  pcpi_wait,
-	input		  pcpi_ready,
+	output reg		       pcpi_valid,
+	output reg [31:0]	       pcpi_insn,
+	output [31:0]		       pcpi_rs1,
+	output [31:0]		       pcpi_rs2,
+	input			       pcpi_wr,
+	input [31:0]		       pcpi_rd,
+	input			       pcpi_wait,
+	input			       pcpi_ready,
 
 	// IRQ Interface
-	input [31:0]	  irq,
-	output reg [31:0] eoi,
+	input [31:0]		       irq,
+	output reg [31:0]	       eoi,
+
+	// user_context export
+	output reg [context_max_bit:0] user_context,
 
 `ifdef RISCV_FORMAL
-	output reg	  rvfi_valid,
-	output reg [63:0] rvfi_order,
-	output reg [31:0] rvfi_insn,
-	output reg	  rvfi_trap,
-	output reg	  rvfi_halt,
-	output reg	  rvfi_intr,
-	output reg [ 1:0] rvfi_mode,
-	output reg [ 1:0] rvfi_ixl,
-	output reg [ 4:0] rvfi_rs1_addr,
-	output reg [ 4:0] rvfi_rs2_addr,
-	output reg [31:0] rvfi_rs1_rdata,
-	output reg [31:0] rvfi_rs2_rdata,
-	output reg [ 4:0] rvfi_rd_addr,
-	output reg [31:0] rvfi_rd_wdata,
-	output reg [31:0] rvfi_pc_rdata,
-	output reg [31:0] rvfi_pc_wdata,
-	output reg [31:0] rvfi_mem_addr,
-	output reg [ 3:0] rvfi_mem_rmask,
-	output reg [ 3:0] rvfi_mem_wmask,
-	output reg [31:0] rvfi_mem_rdata,
-	output reg [31:0] rvfi_mem_wdata,
-
-	output reg [63:0] rvfi_csr_mcycle_rmask,
-	output reg [63:0] rvfi_csr_mcycle_wmask,
-	output reg [63:0] rvfi_csr_mcycle_rdata,
-	output reg [63:0] rvfi_csr_mcycle_wdata,
-
-	output reg [63:0] rvfi_csr_minstret_rmask,
-	output reg [63:0] rvfi_csr_minstret_wmask,
-	output reg [63:0] rvfi_csr_minstret_rdata,
-	output reg [63:0] rvfi_csr_minstret_wdata,
+	output reg		       rvfi_valid,
+	output reg [63:0]	       rvfi_order,
+	output reg [31:0]	       rvfi_insn,
+	output reg		       rvfi_trap,
+	output reg		       rvfi_halt,
+	output reg		       rvfi_intr,
+	output reg [ 1:0]	       rvfi_mode,
+	output reg [ 1:0]	       rvfi_ixl,
+	output reg [ 4:0]	       rvfi_rs1_addr,
+	output reg [ 4:0]	       rvfi_rs2_addr,
+	output reg [31:0]	       rvfi_rs1_rdata,
+	output reg [31:0]	       rvfi_rs2_rdata,
+	output reg [ 4:0]	       rvfi_rd_addr,
+	output reg [31:0]	       rvfi_rd_wdata,
+	output reg [31:0]	       rvfi_pc_rdata,
+	output reg [31:0]	       rvfi_pc_wdata,
+	output reg [31:0]	       rvfi_mem_addr,
+	output reg [ 3:0]	       rvfi_mem_rmask,
+	output reg [ 3:0]	       rvfi_mem_wmask,
+	output reg [31:0]	       rvfi_mem_rdata,
+	output reg [31:0]	       rvfi_mem_wdata,
+
+	output reg [63:0]	       rvfi_csr_mcycle_rmask,
+	output reg [63:0]	       rvfi_csr_mcycle_wmask,
+	output reg [63:0]	       rvfi_csr_mcycle_rdata,
+	output reg [63:0]	       rvfi_csr_mcycle_wdata,
+
+	output reg [63:0]	       rvfi_csr_minstret_rmask,
+	output reg [63:0]	       rvfi_csr_minstret_wmask,
+	output reg [63:0]	       rvfi_csr_minstret_rdata,
+	output reg [63:0]	       rvfi_csr_minstret_wdata,
 `endif
 
 	// Trace Interface
-	output reg	  trace_valid,
-	output reg [35:0] trace_data
+	output reg		       trace_valid,
+	output reg [35:0]	       trace_data
 );
 	localparam integer irq_timer = 0;
 	localparam integer irq_ebreak = 1;
@@ -213,14 +227,11 @@ module picorv32 #(
 	localparam integer xreg_count   = ENABLE_REGS_16_31 ? 32 : 16;
         localparam integer xreg_bits    = $clog2(xreg_count);
         localparam integer xreg_banks   = USER_CONTEXTS + 1;
-        localparam integer context_bits = $clog2(xreg_banks);
         localparam integer regfile_size = xreg_count * xreg_banks;
         localparam integer regfile_bits = $clog2(regfile_size);
 	wire [regfile_bits-1:0] xreg_mask = xreg_count - 1;
 
-        reg [context_bits-1:0]	user_context;
-
-        wire [regfile_bits-1:0]		xreg_offset;
+        wire [regfile_bits-1:0]	xreg_offset;
         assign xreg_offset[regfile_bits-1:xreg_bits] = irq_active ? 0 : user_context;
 	assign xreg_offset[xreg_bits-1:0] = 0;
 
@@ -445,7 +456,7 @@ module picorv32 #(
 	assign mem_la_write = resetn && !mem_state && mem_do_wdata;
 	assign mem_la_read = resetn && ((!mem_la_use_prefetched_high_word && !mem_state && (mem_do_rinst || mem_do_prefetch || mem_do_rdata)) ||
 			(COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg) && !mem_la_secondword && &mem_rdata_latched[1:0]));
-	assign mem_la_addr = (mem_do_prefetch || mem_do_rinst) ? {next_pc[31:2] + mem_la_firstword_xfer, 2'b00} : {reg_op1[31:2], 2'b00};
+        assign mem_la_addr = (mem_do_prefetch || mem_do_rinst) ? {next_pc[31:2] + mem_la_firstword_xfer, 2'b00} : reg_op1;
 
 	assign mem_rdata_latched_noshuffle = (mem_xfer || LATCHED_MEM_RDATA) ? mem_rdata : mem_rdata_q;
 
@@ -1748,13 +1759,14 @@ module picorv32 #(
 					        // Bitops not supported ATM, treat as readonly
 					        if (~instr_funct2[1])
 						  case (decoded_imm[11:0])
-						    12'h341: begin		        // mepc
+						    12'h341: if (ENABLE_IRQ) begin // mepc
 						       reg_mepc <= csrr_src;
 						    end
-						    12'h7f0: begin			// user_context
-						       user_context <= csrr_src;
-						       irq_active   <= 1'b1;
-						    end
+						    12'h7f0:
+						      if (USER_CONTEXTS > 0) begin
+							 user_context <= csrr_src;
+							 irq_active   <= 1'b1;
+						      end
 						    default: begin
 						       // Do nothing
 						    end

+ 1 - 1
fpga/sdram.sv

@@ -104,7 +104,7 @@ module dram_port
    reg started;
 
    assign bus.prio  = prio;
-   assign bus.addr  = addr;
+   assign bus.addr  = addr & ~((width - 1) >> 3);
    assign bus.req   = valid & ~started;
 
    always_comb