//////////////////////////////////////////////////////////////////////////////// // // 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< 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 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