326 lines
10 KiB
Verilog
326 lines
10 KiB
Verilog
/*****************************************************************************\
|
|
| Copyright (C) 2021-2022 Luke Wren |
|
|
| SPDX-License-Identifier: Apache-2.0 |
|
|
\*****************************************************************************/
|
|
|
|
// Single-ported top level file for Hazard3 CPU. This file instantiates the
|
|
// Hazard3 core, and arbitrates its instruction fetch and load/store signals
|
|
// down to a single AHB5 master port.
|
|
|
|
`default_nettype none
|
|
|
|
module hazard3_cpu_1port #(
|
|
`include "hazard3_config.vh"
|
|
) (
|
|
// Global signals
|
|
input wire clk,
|
|
input wire clk_always_on,
|
|
input wire rst_n,
|
|
|
|
`ifdef RISCV_FORMAL
|
|
`RVFI_OUTPUTS ,
|
|
`endif
|
|
|
|
// Power control signals
|
|
output wire pwrup_req,
|
|
input wire pwrup_ack,
|
|
output wire clk_en,
|
|
output wire unblock_out,
|
|
input wire unblock_in,
|
|
|
|
// AHB5 Master port
|
|
output reg [W_ADDR-1:0] haddr,
|
|
output reg hwrite,
|
|
output reg [1:0] htrans,
|
|
output reg [2:0] hsize,
|
|
output wire [2:0] hburst,
|
|
output reg [3:0] hprot,
|
|
output wire hmastlock,
|
|
output reg [7:0] hmaster,
|
|
output reg hexcl,
|
|
input wire hready,
|
|
input wire hresp,
|
|
input wire hexokay,
|
|
output wire [W_DATA-1:0] hwdata,
|
|
input wire [W_DATA-1:0] hrdata,
|
|
|
|
// Debugger run/halt control
|
|
input wire dbg_req_halt,
|
|
input wire dbg_req_halt_on_reset,
|
|
input wire dbg_req_resume,
|
|
output wire dbg_halted,
|
|
output wire dbg_running,
|
|
// Debugger access to data0 CSR
|
|
input wire [W_DATA-1:0] dbg_data0_rdata,
|
|
output wire [W_DATA-1:0] dbg_data0_wdata,
|
|
output wire dbg_data0_wen,
|
|
// Debugger instruction injection
|
|
input wire [W_DATA-1:0] dbg_instr_data,
|
|
input wire dbg_instr_data_vld,
|
|
output wire dbg_instr_data_rdy,
|
|
output wire dbg_instr_caught_exception,
|
|
output wire dbg_instr_caught_ebreak,
|
|
|
|
// Optional debug system bus access patch-through
|
|
input wire [W_ADDR-1:0] dbg_sbus_addr,
|
|
input wire dbg_sbus_write,
|
|
input wire [1:0] dbg_sbus_size,
|
|
input wire dbg_sbus_vld,
|
|
output wire dbg_sbus_rdy,
|
|
output wire dbg_sbus_err,
|
|
input wire [W_DATA-1:0] dbg_sbus_wdata,
|
|
output wire [W_DATA-1:0] dbg_sbus_rdata,
|
|
|
|
// Level-sensitive interrupt sources
|
|
input wire [NUM_IRQS-1:0] irq, // -> mip.meip
|
|
input wire soft_irq, // -> mip.msip
|
|
input wire timer_irq // -> mip.mtip
|
|
);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Processor core
|
|
|
|
// Instruction fetch signals
|
|
wire core_aph_req_i;
|
|
wire core_aph_panic_i;
|
|
wire core_aph_ready_i;
|
|
wire core_dph_ready_i;
|
|
wire core_dph_err_i;
|
|
|
|
wire [W_ADDR-1:0] core_haddr_i;
|
|
wire [2:0] core_hsize_i;
|
|
wire core_priv_i;
|
|
wire [W_DATA-1:0] core_rdata_i;
|
|
|
|
|
|
// Load/store signals
|
|
wire core_aph_req_d;
|
|
wire core_aph_excl_d;
|
|
wire core_aph_ready_d;
|
|
wire core_dph_ready_d;
|
|
wire core_dph_err_d;
|
|
wire core_dph_exokay_d;
|
|
|
|
wire [W_ADDR-1:0] core_haddr_d;
|
|
wire [2:0] core_hsize_d;
|
|
wire core_priv_d;
|
|
wire core_hwrite_d;
|
|
wire [W_DATA-1:0] core_wdata_d;
|
|
wire [W_DATA-1:0] core_rdata_d;
|
|
|
|
|
|
hazard3_core #(
|
|
`include "hazard3_config_inst.vh"
|
|
) core (
|
|
.clk (clk),
|
|
.clk_always_on (clk_always_on),
|
|
.rst_n (rst_n),
|
|
|
|
.pwrup_req (pwrup_req),
|
|
.pwrup_ack (pwrup_ack),
|
|
.clk_en (clk_en),
|
|
.unblock_out (unblock_out),
|
|
.unblock_in (unblock_in),
|
|
|
|
`ifdef RISCV_FORMAL
|
|
`RVFI_CONN ,
|
|
`endif
|
|
|
|
.bus_aph_req_i (core_aph_req_i),
|
|
.bus_aph_panic_i (core_aph_panic_i),
|
|
.bus_aph_ready_i (core_aph_ready_i),
|
|
.bus_dph_ready_i (core_dph_ready_i),
|
|
.bus_dph_err_i (core_dph_err_i),
|
|
.bus_haddr_i (core_haddr_i),
|
|
.bus_hsize_i (core_hsize_i),
|
|
.bus_priv_i (core_priv_i),
|
|
.bus_rdata_i (core_rdata_i),
|
|
|
|
.bus_aph_req_d (core_aph_req_d),
|
|
.bus_aph_excl_d (core_aph_excl_d),
|
|
.bus_aph_ready_d (core_aph_ready_d),
|
|
.bus_dph_ready_d (core_dph_ready_d),
|
|
.bus_dph_err_d (core_dph_err_d),
|
|
.bus_dph_exokay_d (core_dph_exokay_d),
|
|
.bus_haddr_d (core_haddr_d),
|
|
.bus_hsize_d (core_hsize_d),
|
|
.bus_priv_d (core_priv_d),
|
|
.bus_hwrite_d (core_hwrite_d),
|
|
.bus_wdata_d (core_wdata_d),
|
|
.bus_rdata_d (core_rdata_d),
|
|
|
|
.dbg_req_halt (dbg_req_halt),
|
|
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
|
|
.dbg_req_resume (dbg_req_resume),
|
|
.dbg_halted (dbg_halted),
|
|
.dbg_running (dbg_running),
|
|
.dbg_data0_rdata (dbg_data0_rdata),
|
|
.dbg_data0_wdata (dbg_data0_wdata),
|
|
.dbg_data0_wen (dbg_data0_wen),
|
|
.dbg_instr_data (dbg_instr_data),
|
|
.dbg_instr_data_vld (dbg_instr_data_vld),
|
|
.dbg_instr_data_rdy (dbg_instr_data_rdy),
|
|
.dbg_instr_caught_exception (dbg_instr_caught_exception),
|
|
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
|
|
|
|
|
|
|
|
.irq (irq),
|
|
.soft_irq (soft_irq),
|
|
.timer_irq (timer_irq)
|
|
);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Arbitration state machine
|
|
|
|
wire bus_gnt_i;
|
|
wire bus_gnt_d;
|
|
wire bus_gnt_s;
|
|
|
|
reg bus_hold_aph;
|
|
reg [2:0] bus_gnt_ids_prev;
|
|
|
|
// Note use of clk_always_on: SBA may use this arbiter to access the bus
|
|
// whilst the core is asleep.
|
|
|
|
always @ (posedge clk_always_on or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
bus_hold_aph <= 1'b0;
|
|
bus_gnt_ids_prev <= 3'h0;
|
|
end else begin
|
|
bus_hold_aph <= htrans[1] && !hready && !hresp;
|
|
bus_gnt_ids_prev <= {bus_gnt_i, bus_gnt_d, bus_gnt_s};
|
|
end
|
|
end
|
|
|
|
// Debug SBA access is lower priority than load/store, but higher than
|
|
// instruction fetch. This isn't ideal, but in a tight loop the core may be
|
|
// performing an instruction fetch or load/store on every single cycle, and
|
|
// this is a simple way to guarantee eventual success of debugger accesses. A
|
|
// more complex way would be to add a "panic timer" to boost a stalled sbus
|
|
// access over an instruction fetch.
|
|
|
|
// Note that, often, the sbus will be disconnected: it doesn't provide any
|
|
// increase in debugger bus throughput compared with the program buffer and
|
|
// autoexec. It's useful for "minimally intrusive" debug bus access(i.e. less
|
|
// intrusive than halting the core and resuming it) e.g. for Segger RTT.
|
|
|
|
reg bus_active_dph_s;
|
|
|
|
assign {bus_gnt_i, bus_gnt_d, bus_gnt_s} =
|
|
bus_hold_aph ? bus_gnt_ids_prev :
|
|
core_aph_panic_i ? 3'b100 :
|
|
core_aph_req_d ? 3'b010 :
|
|
dbg_sbus_vld && !bus_active_dph_s ? 3'b001 :
|
|
core_aph_req_i ? 3'b100 :
|
|
3'b000 ;
|
|
reg bus_active_dph_i;
|
|
reg bus_active_dph_d;
|
|
|
|
always @ (posedge clk_always_on or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
bus_active_dph_i <= 1'b0;
|
|
bus_active_dph_d <= 1'b0;
|
|
bus_active_dph_s <= 1'b0;
|
|
end else if (hready) begin
|
|
bus_active_dph_i <= bus_gnt_i;
|
|
bus_active_dph_d <= bus_gnt_d;
|
|
bus_active_dph_s <= bus_gnt_s;
|
|
end
|
|
end
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Address phase request muxing
|
|
|
|
localparam HTRANS_IDLE = 2'b00;
|
|
localparam HTRANS_NSEQ = 2'b10;
|
|
|
|
wire [3:0] hprot_data = {
|
|
2'b00, // Noncacheable/nonbufferable
|
|
core_priv_d, // Privileged or Normal as per core state
|
|
1'b1 // Data access
|
|
};
|
|
|
|
wire [3:0] hprot_instr = {
|
|
2'b00, // Noncacheable/nonbufferable
|
|
core_priv_i, // Privileged or Normal as per core state
|
|
1'b0 // Instruction access
|
|
};
|
|
|
|
wire [3:0] hprot_sbus = {
|
|
2'b00, // Noncacheable/nonbufferable
|
|
1'b1, // Always privileged
|
|
1'b1 // Data access
|
|
};
|
|
|
|
assign hburst = 3'b000; // HBURST_SINGLE
|
|
assign hmastlock = 1'b0;
|
|
|
|
always @ (*) begin
|
|
if (bus_gnt_s) begin
|
|
htrans = HTRANS_NSEQ;
|
|
hexcl = 1'b0;
|
|
haddr = dbg_sbus_addr;
|
|
hsize = {1'b0, dbg_sbus_size};
|
|
hwrite = dbg_sbus_write;
|
|
hprot = hprot_sbus;
|
|
hmaster = 8'h01;
|
|
end else if (bus_gnt_d) begin
|
|
htrans = HTRANS_NSEQ;
|
|
hexcl = core_aph_excl_d;
|
|
haddr = core_haddr_d;
|
|
hsize = core_hsize_d;
|
|
hwrite = core_hwrite_d;
|
|
hprot = hprot_data;
|
|
hmaster = 8'h00;
|
|
end else if (bus_gnt_i) begin
|
|
htrans = HTRANS_NSEQ;
|
|
hexcl = 1'b0;
|
|
haddr = core_haddr_i;
|
|
hsize = core_hsize_i;
|
|
hwrite = 1'b0;
|
|
hprot = hprot_instr;
|
|
hmaster = 8'h00;
|
|
end else begin
|
|
htrans = HTRANS_IDLE;
|
|
hexcl = 1'b0;
|
|
haddr = {W_ADDR{1'b0}};
|
|
hsize = 3'h0;
|
|
hwrite = 1'b0;
|
|
hprot = 4'h0;
|
|
hmaster = 8'h00;
|
|
end
|
|
end
|
|
|
|
assign hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Response routing
|
|
|
|
// Data buses directly connected
|
|
assign core_rdata_d = hrdata;
|
|
assign core_rdata_i = hrdata;
|
|
assign dbg_sbus_rdata = hrdata;
|
|
|
|
// Handhshake based on grant and bus stall
|
|
assign core_aph_ready_i = hready && bus_gnt_i;
|
|
assign core_dph_ready_i = bus_active_dph_i && hready;
|
|
assign core_dph_err_i = bus_active_dph_i && hresp;
|
|
|
|
// D-side errors are reported even when not ready, so that the core can make
|
|
// use of the two-phase error response to cleanly squash a second load/store
|
|
// chasing the faulting one down the pipeline.
|
|
assign core_aph_ready_d = hready && bus_gnt_d;
|
|
assign core_dph_ready_d = bus_active_dph_d && hready;
|
|
assign core_dph_err_d = bus_active_dph_d && hresp;
|
|
assign core_dph_exokay_d = bus_active_dph_d && hexokay;
|
|
|
|
assign dbg_sbus_err = bus_active_dph_s && hresp;
|
|
assign dbg_sbus_rdy = bus_active_dph_s && hready;
|
|
|
|
endmodule
|
|
|
|
`ifndef YOSYS
|
|
`default_nettype wire
|
|
`endif
|