summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/axilfetch.v
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
committerAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
commit3038edc09a2eb15762f2e58533f429489107520b (patch)
treef7a45e424d39e6fef0d59e329c1bf6ea206e2886 /rtl/wb2axip/axilfetch.v
parent3b62399f92e9faa2602ac30865e5fc3c7c4e12b8 (diff)
rtl/wb2axip: add to version control
Diffstat (limited to 'rtl/wb2axip/axilfetch.v')
-rw-r--r--rtl/wb2axip/axilfetch.v469
1 files changed, 469 insertions, 0 deletions
diff --git a/rtl/wb2axip/axilfetch.v b/rtl/wb2axip/axilfetch.v
new file mode 100644
index 0000000..7089b20
--- /dev/null
+++ b/rtl/wb2axip/axilfetch.v
@@ -0,0 +1,469 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilfetch.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: This is a very simple instruction fetch approach based around
+// AXI-lite.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axilfetch #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 64,
+ parameter INSN_WIDTH=32,
+ parameter FETCH_LIMIT=16,
+ parameter [0:0] SWAP_ENDIANNESS = 1'b1,
+ localparam AW=C_AXI_ADDR_WIDTH
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // CPU interaction wires
+ // {{{
+ input wire i_cpu_reset,
+ input wire i_new_pc,
+ input wire i_clear_cache,
+ input wire i_ready,
+ input wire [AW-1:0] i_pc, // Ignd unls i_new_pc
+ output wire [INSN_WIDTH-1:0] o_insn, // Insn read from bus
+ output reg [AW-1:0] o_pc, // Addr of that insn
+ output reg o_valid, // If valid
+ output reg o_illegal, // Bus error
+ // }}}
+ // AXI-lite bus interface
+ // {{{
+ output reg M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output reg [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [2:0] M_AXI_ARPROT,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire [1:0] M_AXI_RRESP
+ // }}}
+ // }}}
+ );
+
+ // Declarations
+ // {{{
+ localparam AXILLSB = $clog2(C_AXI_DATA_WIDTH/8);
+ localparam INSNS_PER_WORD = C_AXI_DATA_WIDTH / INSN_WIDTH;
+ localparam INSN_LSB = $clog2(INSN_WIDTH/8);
+ localparam LGDEPTH = $clog2(FETCH_LIMIT)+4;
+ localparam LGFIFO = $clog2(FETCH_LIMIT);
+ localparam W = LGDEPTH;
+ localparam FILLBITS = $clog2(INSNS_PER_WORD);
+ // ($clog2(INSNS_PER_WORD) > 0)
+ // ? $clog2(INSNS_PER_WORD) : 1);
+
+ reg [W:0] new_flushcount, outstanding,
+ next_outstanding, flushcount;
+ reg flushing, flush_request, full_bus;
+ wire [((AXILLSB>INSN_LSB) ? (AXILLSB-INSN_LSB-1):0):0] shift;
+ wire fifo_reset, fifo_wr, fifo_rd;
+ wire ign_fifo_full, fifo_empty;
+ wire [LGFIFO:0] ign_fifo_fill;
+ wire [C_AXI_DATA_WIDTH:0] fifo_data;
+ reg pending_new_pc;
+ reg [C_AXI_ADDR_WIDTH-1:0] pending_pc;
+ reg [W-1:0] fill;
+ reg [FILLBITS:0] out_fill;
+ reg [C_AXI_DATA_WIDTH-1:0] out_data;
+ reg [C_AXI_DATA_WIDTH-1:0] endian_swapped_rdata;
+ // }}}
+
+ assign fifo_reset = i_cpu_reset || i_clear_cache || i_new_pc;
+ assign fifo_wr = M_AXI_RVALID && !flushing;
+
+
+ // ARPROT = 3'b100 for an unprivileged, secure instruction access
+ // (not sure what unprivileged or secure mean--even after reading the
+ // spec)
+ assign M_AXI_ARPROT = 3'b100;
+
+ // next_outstanding
+ // {{{
+ always @(*)
+ begin
+ next_outstanding = outstanding;
+
+ case({ M_AXI_ARVALID && M_AXI_ARREADY, M_AXI_RVALID })
+ 2'b10: next_outstanding = outstanding + 1;
+ 2'b01: next_outstanding = outstanding - 1;
+ default: begin end
+ endcase
+ end
+ // }}}
+
+ // outstanding, full_bus
+ // {{{
+ initial outstanding = 0;
+ initial full_bus = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ outstanding <= 0;
+ full_bus <= 0;
+ end else begin
+ outstanding <= next_outstanding;
+ full_bus <= (next_outstanding
+ // + (((M_AXI_ARVALID && !M_AXI_ARREADY) ? 1:0)
+ >= (1<<LGDEPTH)-1);
+ end
+ // }}}
+
+ // fill
+ // {{{
+ initial fill = 0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ fill <= 0;
+ // else if (fo_reset || flushing)
+ // fill <= 0;
+ else case({ M_AXI_ARVALID && M_AXI_ARREADY && !flush_request,
+ fifo_rd && !fifo_empty })
+ 2'b10: fill <= fill + 1;
+ 2'b01: fill <= fill - 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // new_flushcount
+ // {{{
+ always @(*)
+ new_flushcount = outstanding + (M_AXI_ARVALID ? 1:0)
+ - (M_AXI_RVALID ? 1:0);
+ // }}}
+
+ // flushcount, flushing, flush_request
+ // {{{
+ initial flushcount = 0;
+ initial flushing = 0;
+ initial flush_request = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ flushcount <= 0;
+ flushing <= 0;
+ flush_request <= 0;
+ end else if (fifo_reset)
+ begin
+ flushcount <= new_flushcount;
+ flushing <= (new_flushcount > 0);
+ flush_request <= (M_AXI_ARVALID && !M_AXI_ARREADY);
+ end else begin
+ if (M_AXI_RVALID && flushcount > 0)
+ begin
+ flushcount <= flushcount - 1;
+ // Verilator lint_off CMPCONST
+ flushing <= (flushcount > 1);
+ // Verilator lint_on CMPCONST
+ end
+
+ if (M_AXI_ARREADY)
+ flush_request <= 0;
+ end
+ // }}}
+
+ // M_AXI_ARVALID
+ // {{{
+ initial M_AXI_ARVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXI_ARVALID <= 1'b0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ M_AXI_ARVALID <= 1;
+ if (i_new_pc || pending_new_pc)
+ M_AXI_ARVALID <= 1'b1;
+
+ //
+ // Throttle the number of requests we make
+ // Verilator lint_off CMPCONST
+ // Verilator lint_off WIDTH
+ // out_fill will only capture 0 or 1 if DATA_WIDTH == 32
+ if (fill + (M_AXI_ARVALID ? 1:0)
+ + ((o_valid &&(!i_ready || out_fill > 1)) ? 1:0)
+ >= FETCH_LIMIT)
+ M_AXI_ARVALID <= 1'b0;
+ // Verilator lint_on WIDTH
+ // Verilator lint_on CMPCONST
+ if (i_cpu_reset || i_clear_cache || full_bus)
+ M_AXI_ARVALID <= 1'b0;
+ end
+ // }}}
+
+ assign M_AXI_RREADY = 1'b1;
+
+ // pending_new_pc
+ // {{{
+ initial pending_new_pc = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || i_clear_cache)
+ pending_new_pc <= 1'b0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ pending_new_pc <= 1'b0;
+ else if (i_new_pc)
+ pending_new_pc <= 1'b1;
+ // }}}
+
+ // pending_pc
+ // {{{
+ initial pending_pc = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_new_pc)
+ pending_pc <= i_pc;
+ // }}}
+
+ // M_AXI_ARADDR
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ if (i_new_pc)
+ M_AXI_ARADDR <= i_pc;
+ else if (pending_new_pc)
+ M_AXI_ARADDR <= pending_pc;
+ else if (M_AXI_ARVALID)
+ begin
+ M_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:AXILLSB]
+ <= M_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:AXILLSB] +1;
+ M_AXI_ARADDR[AXILLSB-1:0] <= 0;
+ end
+ end
+ // }}}
+
+ // o_pc
+ // {{{
+ initial o_pc = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_new_pc)
+ o_pc <= i_pc;
+ else if (o_valid && i_ready && !o_illegal)
+ begin
+ o_pc <= 0;
+ o_pc[AW-1:INSN_LSB] <= o_pc[AW-1:INSN_LSB] + 1;
+ end
+ // }}}
+
+ generate if (AXILLSB > INSN_LSB)
+ begin : BIG_WORD
+ // {{{
+ assign shift = o_pc[AXILLSB-1:INSN_LSB];
+ // }}}
+ end else begin : NO_SHIFT
+ // {{{
+ assign shift = 0;
+ // }}}
+ end endgenerate
+
+ generate if (SWAP_ENDIANNESS)
+ begin : SWAPPED_ENDIANNESS
+ // {{{
+ genvar gw, gb; // Word count, byte count
+
+ for(gw=0; gw<C_AXI_DATA_WIDTH/INSN_WIDTH; gw=gw+1) // For each bus word
+ for(gb=0; gb<(INSN_WIDTH/8); gb=gb+1) // For each bus byte
+ always @(*)
+ endian_swapped_rdata[gw*INSN_WIDTH
+ + ((INSN_WIDTH/8)-1-gb)*8 +: 8]
+ = M_AXI_RDATA[gw*INSN_WIDTH+gb*8 +: 8];
+ // }}}
+ end else begin : NO_ENDIAN_SWAP
+ // {{{
+ always @(*)
+ endian_swapped_rdata = M_AXI_RDATA;
+ // }}}
+ end endgenerate
+
+ generate if (FETCH_LIMIT <= 1)
+ begin : NOCACHE
+ // {{{
+ // No cache
+
+ // assign fifo_rd = fifo_wr;
+ // Verilator lint_off CMPCONST
+ assign fifo_rd = !o_valid || (i_ready && (out_fill <= 1));
+ // Verilator lint_on CMPCONST
+ assign fifo_empty = !fifo_wr; //(out_fill <= (i_aready ? 1:0));
+ assign fifo_data = { M_AXI_RRESP[1], endian_swapped_rdata };
+
+ assign ign_fifo_fill = 1'b0;
+ assign ign_fifo_full = 1'b0;
+`ifdef FORMAL
+ always @(*)
+ if (M_AXI_RVALID || M_AXI_ARVALID || outstanding > 0)
+ assert(!o_valid);
+`endif
+ // }}}
+ end else if (FETCH_LIMIT == 2)
+ begin : DBLFETCH
+ // {{{
+ // Single word cache
+ reg cache_valid;
+ reg [C_AXI_DATA_WIDTH:0] cache_data;
+
+ assign fifo_rd = !o_valid || (i_ready && (out_fill <= 1));
+ assign fifo_empty =(!M_AXI_RVALID && !cache_valid) || flushing;
+ assign fifo_data = cache_valid ? cache_data
+ : ({ M_AXI_RRESP[1], endian_swapped_rdata });
+
+
+ assign ign_fifo_fill = cache_valid ? 1 : 0;
+ assign ign_fifo_full = cache_valid;
+
+ initial cache_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ cache_valid <= 1'b0;
+ else if (M_AXI_RVALID && o_valid && !fifo_rd)
+ cache_valid <= 1;
+ else if (fifo_rd)
+ cache_valid <= 1'b0;
+
+ always @(posedge S_AXI_ACLK)
+ if (M_AXI_RVALID)
+ cache_data <= { M_AXI_RRESP[1], endian_swapped_rdata };
+
+ // Make Verilator happy
+ // {{{
+ wire unused_dblfetch;
+ assign unused_dblfetch = &{ 1'b0, fifo_wr };
+ // }}}
+ // }}}
+ end else begin : FIFO_FETCH
+ // {{{
+ // FIFO cache
+
+ // Verilator lint_off CMPCONST
+ // out_fill will only capture 0 or 1 if DATA_WIDTH == 32
+ assign fifo_rd = !o_valid || (i_ready && (out_fill <= 1));
+ // Verilator lint_on CMPCONST
+
+ sfifo #(
+ // {{{
+ .BW(1+C_AXI_DATA_WIDTH), .LGFLEN(LGFIFO)
+ // }}}
+ ) fcache(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(fifo_reset),
+ .i_wr(fifo_wr),
+ .i_data({M_AXI_RRESP[1], endian_swapped_rdata }),
+ .o_full(ign_fifo_full), .o_fill(ign_fifo_fill),
+ .i_rd(fifo_rd),.o_data(fifo_data),.o_empty(fifo_empty)
+ // }}}
+ );
+ // }}}
+ end endgenerate
+
+ // o_valid
+ // {{{
+ initial o_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ o_valid <= 1'b0;
+ else if (!o_valid || i_ready)
+ o_valid <= (fifo_rd && !fifo_empty)
+ || out_fill > (o_valid ? 1:0);
+ // }}}
+
+ // out_fill
+ // {{{
+ // == number of instructions in the fifo_data word that have not (yet)
+ // been accepted by the CPU.
+ // == 0 when no data is available
+ // == INSN_PER_WORD on the first instruction of any word
+ // == 1 on the last instruction of any word
+ initial out_fill = 0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ out_fill <= 0;
+ else if (fifo_rd)
+ begin
+ if (fifo_empty)
+ out_fill <= 0;
+ else if (o_valid)
+ out_fill <= INSNS_PER_WORD[FILLBITS:0];
+ else
+ // Verilator lint_off WIDTH
+ out_fill <= (INSNS_PER_WORD[FILLBITS:0] - shift);
+ // Verilator lint_on WIDTH
+ end else if (i_ready && out_fill > 0)
+ out_fill <= out_fill - 1;
+ // }}}
+
+ // out_data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (fifo_rd)
+ begin
+ if (o_valid || (INSN_WIDTH == C_AXI_DATA_WIDTH))
+ out_data <= fifo_data[C_AXI_DATA_WIDTH-1:0];
+ else
+ out_data <= fifo_data[C_AXI_DATA_WIDTH-1:0]>>(INSN_WIDTH*shift);
+ end else if (i_ready)
+ out_data <= out_data >> INSN_WIDTH;
+ // }}}
+
+ assign o_insn = out_data[INSN_WIDTH-1:0];
+
+ // o_illegal
+ // {{{
+ initial o_illegal = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ o_illegal <= 1'b0;
+ else if (!o_illegal && fifo_rd && !fifo_empty)
+ o_illegal <= fifo_data[C_AXI_DATA_WIDTH];
+ // }}}
+
+ // Make verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = & { 1'b0, M_AXI_RRESP[0], ign_fifo_full, ign_fifo_fill };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Formal properties for this module are maintained elsewhere
+`endif
+endmodule