diff --git a/docs/ip-briefs/plic_module_brief_v0.2.md b/docs/ip-briefs/plic_module_brief_v0.2.md index b0b4c65..24f5a83 100644 --- a/docs/ip-briefs/plic_module_brief_v0.2.md +++ b/docs/ip-briefs/plic_module_brief_v0.2.md @@ -1,16 +1,16 @@ -# PLIC - Module Brief (v1.0) +# PLIC - Module Brief (v0.3) **Owner:** Gavin Wiese **RTL:** rtl/irq/plic.sv ### Purpose & Role -The Platform-Level Interrupt Controller (PLIC) is placed near the CPU core. The PLIC manages and prioritizes external interrupt requests from up to `NSOURCES` sources, forwarding only the highest-priority pending interrupt to the CPU. This allows the processor to efficiently handle asynchronous external events. +The Platform-Level Interrupt Controller (PLIC) is placed near the CPU core. The PLIC manages and prioritizes external interrupt requests from up to 32 sources, forwarding only the highest-priority pending interrupt to the CPU. This allows the processor to efficiently handle asynchronous external events. ### Parameters -- Number of interrupt sources: `NSOURCES` (default 8) -- Priority field width per source: `PRIO_WIDTH` (default 3 bits) -- Interrupt ID width: `SRC_ID_WIDTH` (default 3 bits) +- Number of interrupt sources: 32 (`NSOURCES`) +- Priority field width per source: 3 bits (`PRIO_WIDTH`) +- Interrupt ID width for claim/complete operations: `$clog2(NSOURCES)` ### Interfaces (Ports) @@ -18,57 +18,52 @@ The Platform-Level Interrupt Controller (PLIC) is placed near the CPU core. The |----------------------|---------|-----------|----------------------------------------------------| | clk_i | in | 1 | System clock | | rst_ni | in | 1 | Active-low asynchronous reset | -| claim_req_i | in | 1 | CPU claim request pulse | -| complete_i | in | 1 | CPU interrupt completion pulse | -| src_i | in | NSOURCES | External interrupt sources | -| priority_wdata | in | NSOURCES × PRIO_WIDTH | Data to write to all priority registers | +| src_i | in | 32 | External interrupt sources | +| priority_wdata | in | 96 | Data to write to all priority registers (32 × 3) | | priority_we | in | 1 | Write enable for priority registers | -| enable_wdata | in | NSOURCES | Data to write to enable register | +| enable_wdata | in | 32 | Data to write to enable register | | enable_we | in | 1 | Write enable for enable register | +| claim_wdata | in | 5 | Claim complete input | +| claim_we | in | 1 | Write enable for claim completion | | ext_irq_o | out | 1 | Interrupt output to the CPU core | -| claim_o | out | SRC_ID_WIDTH | Current claimed interrupt ID (1-based, 0 = none) | +| claim_o | out | 5 | Current claimed interrupt ID | ### Reset/Init -An active-low asynchronous reset (`rst_ni`) is used for the PLIC. When reset is asserted (`rst_ni = 0`), all internal registers—including `priorities`, `enable`, `pending`, and claim state—are cleared to 0, and the output signal `ext_irq_o` is deasserted. +An active-low asynchronous reset (`rst_ni`) is used for the PLIC. When reset is asserted (`rst_ni = 0`), all internal registers—including `priorities`, `enable`, `pending`, and `claim`—are cleared to 0, and the output signal `ext_irq_o` is deasserted. ### Behavior and Timing -The PLIC continuously monitors the `src_i` interrupt lines. When one or more enabled interrupts are pending with nonzero priority and no interrupt is currently in service, the highest-priority source is selected and `ext_irq_o` is asserted to signal the CPU core. - -When the CPU asserts `claim_req_i`, the PLIC outputs the highest-priority enabled pending interrupt ID on `claim_o` (1-based) and clears that interrupt’s pending bit. While an interrupt is in service, `ext_irq_o` remains deasserted. - -When the CPU signals completion via `complete_i`, the in-service state is cleared, allowing the next pending interrupt (if any) to be delivered. - -All operations are synchronous with the system clock. +The PLIC continuously monitors the 32 `src_i` interrupt lines. When one or more enabled interrupts are pending, the highest-priority source is selected, and `ext_irq_o` is asserted to signal the CPU core. Once the CPU completes the interrupt (signaled via `claim_we`), the pending bit for that interrupt is cleared and `ext_irq_o` deasserts. All operations are synchronous with the system clock, and `ext_irq_o` asserts one clock cycle after the conditions are met. ### Programming Model -The PLIC provides two sets of registers controlled via simple write-enable/data inputs: +The PLIC provides three sets of registers controlled via simple write-enable/data inputs: - **`priority`** – Stores the priority of each interrupt source. Higher values indicate higher priority. Updated via `priority_wdata` and `priority_we`. - **`enable`** – Determines which interrupt sources are enabled. Updated via `enable_wdata` and `enable_we`. +- **`claim`** – Contains the currently claimed interrupt ID. Writing to this register with `claim_wdata` and `claim_we` signals completion, clearing the pending bit. -Claim and completion are handled via `claim_req_i` and `complete_i` handshake signals. +All registers are accessible through the `_we` / `_wdata` inputs in this bus-free implementation. ### Errors/IRQs | **IRQ** | **Source** | **Trigger** | **Clear** | |------------|-----------|----------------------------------|-------------------------------------| -| ext_irq_o | src_i | One or more enabled interrupts pending with priority > 0 and no active claim | Cleared while in service; may reassert after completion | +| ext_irq_o | src_i | One or more enabled interrupts pending | Cleared when CPU signals completion via claim input | The PLIC does not generate additional internal error signals; all interrupts come from external sources. ### Performance Targets -- `ext_irq_o` asserts when an enabled pending interrupt with nonzero priority exists and no interrupt is currently in service. -- All internal registers (priority, enable, pending, claim state) update synchronously with the system clock. -- The PLIC can handle all `NSOURCES` external sources without loss of pending interrupts. +- `ext_irq_o` asserts within one clock cycle of a pending, enabled interrupt being detected. +- All internal registers (priority, enable, claim/complete, pending) update synchronously with the system clock. +- The PLIC can handle all 32 external sources without loss of pending interrupts. ### Dependencies -The PLIC depends on `clk_i` to update internal registers and monitor interrupt sources, and on `rst_ni` to initialize registers. External interrupt lines (`src_i`) provide input events, and the PLIC drives the single interrupt output (`ext_irq_o`) to the CPU core. Register updates are controlled via `_we` / `_wdata` inputs, and interrupt servicing uses the `claim_req_i` / `complete_i` handshake. +The PLIC depends on `clk_i` to update internal registers and monitor interrupt sources, and on `rst_ni` to initialize registers. External interrupt lines (`src_i`) provide input events, and the PLIC drives the single interrupt output (`ext_irq_o`) to the CPU core. Register updates are controlled via `_we` / `_wdata` inputs. ### Verification Links -Verification for the PLIC is planned through simulation testbenches to confirm correct behavior of priority handling, enable bits, and the claim/complete mechanism. Testbenches will ensure that `ext_irq_o` asserts for the highest-priority pending interrupt, that pending bits are cleared after a claim operation, and that the module responds correctly to reset (`rst_ni`). +Verification for the PLIC is planned through simulation testbenches to confirm correct behavior of priority handling, enable bits, and the claim/complete mechanism. Testbenches will ensure that `ext_irq_o` asserts for the highest-priority pending interrupt, that pending bits are cleared after a claim/complete operation, and that the module responds correctly to reset (`rst_ni`). diff --git a/rtl/bus/axi/axi_dcache_port.sv b/rtl/bus/axi/axi_dcache_port.sv index 2859d17..70384f4 100644 --- a/rtl/bus/axi/axi_dcache_port.sv +++ b/rtl/bus/axi/axi_dcache_port.sv @@ -1,15 +1,478 @@ -module axi_dcache_port #( - // TODO: Parameter setups - parameter +module axi_dcache_port + +// Parameters subject to change? +import interconnect_pkg::*; +#( + /*AXI_ADDR_WIDTH = 32, + AXI_DATA_WIDTH = 64, + AXI_ID_WIDTH = 4, + AXI_USER_WIDTH = 1, + AXI_STRB_WIDTH = AXI_DATA_WIDTH/8, + AXI_BURST_LEN = (AXI_DATA_WIDTH / 8), + AXI_BURST_SIZE = $clog2(AXI_BURST_LEN), + */ )( - // TODO: Port set up - ports - // TODO: Input logic - input logic clk_i, - input logic rst_ni - + // Global & Control Signals + input logic m_clk_i, // Master (main) clock signal + input logic s_clk_i, // Slave clock signal + input logic rst_ni, + + // Write Address (AW) Channel + output logic [AXI_ID_WIDTH-1:0] aw_id_o, // ID Tag + output logic [AXI_ADDR_WIDTH-1:0] aw_addr_o, // Addr. of first transfer in write burst + output logic [AXI_BURST_LEN-1:0] aw_len_o, // No. of transfers in a burst + output logic [AXI_BURST_SIZE-1:0] aw_size_o, // Bytes per beats + output logic [1:0] aw_burst_o, // Burst type (fixed, incr, wrap) + output logic aw_valid_o, // Valid write addr. + input logic aw_ready_i, // Slave ready to accept addr. + input logic aw_fifo_full_i, // FIFO full indicator + input logic aw_fifo_empty_i, // FIFO empty indicator + + // Write Data (W) Channel + output logic [DATA_WIDTH*8-1:0] w_data_o, // Write data + output logic [AXI_STRB_WIDTH-1:0] w_strb_o, // Byte lane indicator + output logic w_last_o, // Last transfer in write burst + output logic w_valid_o, // Write data available + input logic w_ready_i, // Slave can accept write data + input logic w_fifo_full_i, // FIFO full indicator + input logic w_fifo_empty_i, // FIFO empty indicator + + // Write Response (B) Channel + input logic [ID_WIDTH-1:0] b_id_i, // ID tag of write response + input logic b_valid_i, // Slave signaling valid response + output logic b_ready_o, // Master can accept write response + input logic b_resp_i, // transaction status (might be unnecessary) + input logic b_fifo_full_i, // FIFO full indicator + input logic b_fifo_empty_i, // FIFO empty indicator + + // Read Address (AR) Channel + output logic [ID_WIDTH-1:0] ar_id_o, // ID tag for AR + output logic [ADDR_WIDTH-1:0] ar_addr_o, // Addr. of first transfer in read burst + output logic [AXI_BURST_LEN-1:0] ar_len_o, // No. of transfers in a burst + output logic [AXI_BURST_SIZE:0] ar_size_o, // Bytes per beats + output logic [1:0] ar_burst_o, // Burst type (fixed, incr, wrap) + output logic ar_valid_o, // Valid read addr. + input logic ar_ready_i, // Slave ready to accept addr. + input logic ar_fifo_full_i, // FIFO full indicator + input logic ar_fifo_empty_i, // FIFO empty indicator + + // Read Data (R) Channel + input logic [ID_WIDTH-1:0] r_id_i, // ID tag for R + input logic [DATA_WIDTH*8-1:0] r_data_i, // Read data + input logic r_valid_i, // Slave signaling valid response + output logic r_ready_o, // Master can accept read data + input logic r_last_i, // Last transfer in read burst + input logic [1:0] r_resp_i, // read trans. status (might be unncessary) + input logic r_fifo_full_i, // FIFO full indicator + input logic r_fifo_empty_i, // FIFO empty indicator + + //D$ <-> AXI + input logic dcache_axi_req_valid_i, + output logic axi_dcache_req_ready_o, + input logic dcache_wb_e, + input logic [ADDR_WIDTH-1:0] dcache_axi_req_addr_i, + input logic [DATA_WIDTH*8-1:0] dcache_axi_req_data_i, + output logic axi_dcache_resp_valid_o, + input logic axi_dcache_resp_ready_i, + output logic [DATA_WIDTH*8-1:0] axi_dcache_resp_data_o ); -endmodule +// Registers for latching addr/data from D$ +logic [ADDR_WIDTH-1:0] dcache_addr_latched; +logic [DATA_WIDTH*8-1:0] dcache_data_latched; +logic addr_data_latched_w; + +logic [ADDR_WIDTH-1:0] dcache_addr_latched_r; +logic addr_latched_r; + +// Register for tracking beats in a transaction +logic [AXI_BURST_LEN-1:0] beats; + +// Flags for writeback (B) response +logic write_resp; +logic write_error; + +// Flags for pushing / popping from respective FIFOs +logic aw_fifo_wr_push_o; +logic w_fifo_wr_push_o; +logic b_fifo_rd_pop_o; +logic ar_fifo_wr_push_o; +logic r_fifo_rd_pop_o; + +typedef struct packed { + logic [AXI_ID_WIDTH-1:0] id; + logic [AXI_ADDR_WIDTH-1:0] addr; + logic [AXI_BURST_LEN-1:0] len; + logic [AXI_BURST_SIZE-1:0] size; + logic [1:0] burst; +} axi_aw_payload_t; + +typedef struct packed { + logic [DATA_WIDTH*8-1:0] data; + logic [AXI_STRB_WIDTH-1:0] strb; + logic last; +} axi_w_payload_t; + +typedef struct packed { + logic [ID_WIDTH-1:0] id; + logic [1:0] resp; +} axi_b_payload_t; + +typedef struct packed { + logic [ID_WIDTH-1:0] id; + logic [ADDR_WIDTH-1:0] addr; + logic [AXI_BURST_LEN-1:0] len; + logic [AXI_BURST_SIZE:0] size; + logic [1:0] burst; +} axi_ar_payload_t; + +typedef struct packed { + logic [ID_WIDTH-1:0] id; + logic [DATA_WIDTH*8-1:0] data; + logic last; + logic resp; +} axi_r_payload_t; + +// Instatiate module side payloads +axi_aw_payload_t aw_wr_s; +axi_w_payload_t w_wr_s; +axi_b_payload_t b_rd_s; +axi_ar_payload_t ar_wr_s; +axi_r_payload_t r_rd_s; + +// Construct payloads for module side (ids are defaulted to 0 and does not support multiple transactions...TODO) +assign aw_wr_s = '{id: '0, addr: dcache_addr_latched, len: AXI_BURST_LEN-1, size: AXI_BURST_SIZE, burst: 2'b01}; +assign w_wr_s = '{data: dcache_data_latched, strb: '1, last: (beats == AXI_BURST_LEN-1)}; +assign b_rd_s = '{id: b_id_i, resp: b_resp_i}; +assign ar_wr_s = '{id: '0, addr: dcache_addr_latched_r, len: AXI_BURST_LEN-1, size: AXI_BURST_SIZE, burst: 2'b01}; +assign r_rd_s = '{id: r_id_i, data: r_data_i, last: r_last_i, resp: r_resp_i}; + +// Payloads signals to be passed through respective FIFOs +logic [$bits(axi_aw_payload_t)-1:0] aw_wr_data_o; +logic [$bits(axi_w_payload_t)-1:0] w_wr_data_o; +logic [$bits(axi_b_payload_t)-1:0] b_rd_data_i; +logic [$bits(axi_ar_payload_t)-1:0] ar_wr_data_o; +logic [$bits(axi_r_payload_t)-1:0] r_rd_data_i; + +// Drive struct payloads for use in FIFOs +assign aw_wr_data_o = aw_wr_s; +assign w_wr_data_o = w_wr_s; +assign b_rd_data_i = b_rd_s; +assign ar_wr_data_o = ar_wr_s; +assign r_rd_data_i = r_rd_s; + +// Instatiate FIFOs for each respective channel +axi_fifo #( + .DATA_WIDTH($bits(axi_aw_payload_t)) +) aw_ch_fifo_o ( // Write Address (AW) FIFO + .wr_clk_i(m_clk_i), + .rd_clk_i(s_clk_i), + .rst_ni(rst_ni), + .wr_en_i(aw_fifo_wr_push_o), + .rd_en_i(1'b0), + .wr_data_i(aw_wr_data_o), + .rd_data_o(), + .full_o(aw_fifo_full_i), + .empty_o(aw_fifo_empty_i) +); + +axi_fifo #( + .DATA_WIDTH($bits(axi_w_payload_t)) +) w_ch_fifo_o ( // Write Data (W) FIFO + .wr_clk_i(m_clk_i), + .rd_clk_i(s_clk_i), + .rst_ni(rst_ni), + .wr_en_i(w_fifo_wr_push_o), + .rd_en_i(1'b0), + .wr_data_i(w_wr_data_o), + .rd_data_o(), + .full_o(w_fifo_full_i), + .empty_o(w_fifo_empty_i) +); +axi_fifo #( + .DATA_WIDTH($bits(axi_b_payload_t)) +) b_ch_fifo_i ( // Write Response (B) FIFO + .wr_clk_i(s_clk_i), + .rd_clk_i(m_clk_i), + .rst_ni(rst_ni), + .wr_en_i(1'b0), + .rd_en_i(b_fifo_rd_pop_o), + .wr_data_i(), + .rd_data_o(b_rd_data_i), + .full_o(b_fifo_full_i), + .empty_o(b_fifo_empty_i) +); + +axi_fifo #( + .DATA_WIDTH($bits(axi_ar_payload_t)) +) ar_ch_fifo_o ( // Read Address (AR) FIFO + .wr_clk_i(m_clk_i), + .rd_clk_i(s_clk_i), + .rst_ni(rst_ni), + .wr_en_i(ar_fifo_wr_push_o), + .rd_en_i(1'b0), + .wr_data_i(ar_wr_data_o), + .rd_data_o(), + .full_o(ar_fifo_full_i), + .empty_o(ar_fifo_empty_i) +); + +axi_fifo #( + .DATA_WIDTH($bits(axi_r_payload_t)) +) r_ch_fifo_i ( // Read Data (R) FIFO + .wr_clk_i(s_clk_i), + .rd_clk_i(m_clk_i), + .rst_ni(rst_ni), + .wr_en_i(1'b0), + .rd_en_i(r_fifo_rd_pop_o), + .wr_data_i(), + .rd_data_o(r_rd_data_i), + .full_o(r_fifo_full_i), + .empty_o(r_fifo_empty_i) +); + +// Write FSM Definition +typedef enum logic [2:0] { + W_IDLE, + W_ADDR, + W_DATA, + W_BWAIT, + W_DONE +} axi_write_state_t; +axi_write_state_t w_current_state, w_next_state; + +// Read FSM Definition +typedef enum logic [2:0] { + R_IDLE, + R_ADDR, + R_DATA, + R_DONE +} axi_read_state_t; +axi_read_state_t r_current_state, r_next_state; + + // Next State Sequential Logic + // Reset Condition for both state machines + always_ff @(posedge m_clk_i or negedge rst_ni) begin + if (!rst_ni) begin + w_current_state <= W_IDLE; + r_current_state <= R_IDLE; + end else begin + w_current_state <= w_next_state; + r_current_state <= r_next_state; + end + end + + // Requests are accepted when no transaction is in progress (this will be changed for multiple outstanding transactions) + assign axi_dcache_req_ready_o = (r_current_state == R_IDLE && w_current_state == W_IDLE); + assign axi_dcache_resp_data_o = r_rd_s.data; + + // Sequential block for latching D$ data + always_ff @(posedge m_clk_i or negedge rst_ni) begin + if(!rst_ni) begin + dcache_addr_latched <= '0; + dcache_data_latched <= '0; + addr_data_latched_w <= 1'b0; + end + else if(w_current_state == W_IDLE) begin + if(dcache_axi_req_valid_i && dcache_wb_e && axi_dcache_req_ready_o) begin + dcache_addr_latched <= dcache_axi_req_addr_i; + dcache_data_latched <= dcache_axi_req_data_i; + addr_data_latched_w <= 1'b1; + end + end + end + +// Sequential block for sending write address (AW) + always_ff @(posedge m_clk_i or negedge rst_ni) begin + if(!rst_ni) begin + aw_valid_o <= 1'b0; + aw_fifo_wr_push_o <= 1'b0; + end else begin + aw_valid_o <= 1'b0; + aw_fifo_wr_push_o <= 1'b0; + + if(w_current_state == W_ADDR && !aw_fifo_full_i) begin + aw_valid_o <= 1'b1; + aw_fifo_wr_push_o <= 1'b1; + end + end + end + +// Sequential block for write data (W) + always_ff @(posedge m_clk_i or negedge rst_ni) begin + beats <= '0; + if(!rst_ni) begin + w_valid_o <= 1'b0; + beats <= '0; + w_fifo_wr_push_o <= 1'b0; + end + if(w_current_state == W_DATA && !w_fifo_full_i) begin + w_valid_o <= 1'b1; + w_fifo_wr_push_o <= 1'b1; + + if(beats != AXI_BURST_LEN-1) begin + beats <= beats + 1'b1; + end + else begin + beats <= '0; + end + end + end + + // Sequential block for write response (B) + always_ff @(posedge m_clk_i or negedge rst_ni) begin + write_resp <= 1'b0; + write_error <= 1'b0; + if(!rst_ni) begin + b_ready_o <= 1'b0; + write_resp <= 1'b0; + write_error <= 1'b0; + b_fifo_rd_pop_o <= 1'b0; + end + if(w_current_state == W_BWAIT && !b_fifo_empty_i) begin + b_ready_o <= 1'b1; + b_fifo_rd_pop_o <= 1'b1; + write_resp <= 1'b1; + write_error <= (b_rd_s.resp != 2'b00); + addr_data_latched_w <= 1'b0; + end + end + + //TODO: Implement ID Tracker to handle multiple outstanding transactions + //TODO: Implement handler in event of write respond (B) error (might be unneccesary?) + + // Write State Combinational Block + always_comb begin + w_next_state = w_current_state; + case(w_current_state) + W_IDLE: begin + if(addr_data_latched_w && dcache_wb_e) begin + w_next_state = W_ADDR; + end + end + W_ADDR: begin + if(!aw_fifo_full_i) begin + w_next_state = W_DATA; + end + end + W_DATA: begin + if(!w_fifo_full_i && w_wr_s.last) begin + w_next_state = W_BWAIT; + end + end + W_BWAIT: begin + if(!b_fifo_empty_i) begin + w_next_state = W_DONE; + end + end + W_DONE: begin + if(!write_error) begin + w_next_state = W_IDLE; + end + end + default: begin + w_next_state = W_IDLE; + end + endcase + end + + // Sequential block latching read address + always_ff @(posedge m_clk_i or negedge rst_ni) begin + if (!rst_ni) begin + dcache_addr_latched_r <= '0; + addr_latched_r <= 1'b0; + end + if (r_current_state == R_IDLE) begin + if (dcache_axi_req_valid_i && !dcache_wb_e && !ar_fifo_full_i && axi_dcache_req_ready_o) begin + dcache_addr_latched_r <= dcache_axi_req_addr_i; + addr_latched_r <= 1'b1; + end + end + end + + // Sequential block for read address (AR) + always_ff @(posedge m_clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ar_valid_o <= 1'b0; + ar_fifo_wr_push_o <= 1'b0; + end else begin + ar_valid_o <= 1'b0; + ar_fifo_wr_push_o <= 1'b0; + + if (r_current_state == R_ADDR) begin + if (addr_latched_r && !ar_fifo_full_i) begin + ar_valid_o <= 1'b1; + ar_fifo_wr_push_o <= 1'b1; + end + end + end + end + + // Sequential block for read data (R) + always_ff @(posedge m_clk_i or negedge rst_ni) begin + if (!rst_ni) begin + r_ready_o <= 1'b0; + r_fifo_rd_pop_o <= 1'b0; + end else begin + r_ready_o <= 1'b0; + r_fifo_rd_pop_o <= 1'b0; + + if (r_current_state == R_DATA) begin + if (!r_fifo_empty_i) begin + r_ready_o <= 1'b1; + r_fifo_rd_pop_o <= 1'b1; + end + end + end + end + + // Sequential for returning to dcache (done) + always_ff @(posedge m_clk_i or negedge rst_ni) begin + if (!rst_ni) begin + axi_dcache_resp_valid_o <= 1'b0; + end else if (r_current_state == R_DONE) begin + axi_dcache_resp_valid_o <= 1'b1; + if (axi_dcache_resp_valid_o && axi_dcache_resp_ready_i) begin + axi_dcache_resp_valid_o <= 1'b0; + end + end else begin + axi_dcache_resp_valid_o <= 1'b0; + addr_latched_r <= 1'b0; + end + end + + // Read State Combinational Block + always_comb begin + r_next_state = r_current_state; + case(r_current_state) + R_IDLE: begin + if(dcache_axi_req_valid_i && !dcache_wb_e && !ar_fifo_full_i && addr_latched_r) begin + r_next_state = R_ADDR; + end + else if(addr_latched_r) begin + r_next_state = R_ADDR; + end + end + R_ADDR: begin + if(!ar_fifo_full_i && addr_latched_r) begin + r_next_state = R_DATA; + end + end + R_DATA: begin + if(!r_fifo_empty_i && r_rd_s.last && (r_rd_s.resp == 1'b0)) begin + r_next_state = R_DONE; + end + end + R_DONE: begin + if(axi_dcache_resp_valid_o && axi_dcache_resp_ready_i) begin + r_next_state = R_IDLE; + end + end + default: begin + r_next_state = R_IDLE; + end + endcase + end +endmodule diff --git a/rtl/bus/axi/axi_fifo.sv b/rtl/bus/axi/axi_fifo.sv new file mode 100644 index 0000000..2fb49a5 --- /dev/null +++ b/rtl/bus/axi/axi_fifo.sv @@ -0,0 +1,124 @@ +/* + Asynchronous FIFO + + Synchronizes data flow between two different clock domains with Gray-coded pointers + to greatly reduce metastability. +*/ + +module axi_fifo #( + parameter int DATA_WIDTH = 32, + parameter int FIFO_DEPTH = 8 +)( + input logic wr_clk_i, + input logic rd_clk_i, + input logic rst_ni, + input logic wr_en_i, + input logic rd_en_i, + input logic [DATA_WIDTH-1:0] wr_data_i, + + output logic [DATA_WIDTH-1:0] rd_data_o, + output logic full_o, + output logic empty_o +); + + localparam int PTR_WIDTH = $clog2(FIFO_DEPTH); + localparam int PTR_SIZE = PTR_WIDTH + 1; // extra bit to differentiate between full, empty + + logic [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1]; + + // Write domain pointers + logic [PTR_SIZE-1:0] wr_ptr_bin, wr_ptr_bin_next; + logic [PTR_SIZE-1:0] wr_ptr_gray, wr_ptr_gray_next; + + // Read domain pointers + logic [PTR_SIZE-1:0] rd_ptr_bin, rd_ptr_bin_next; + logic [PTR_SIZE-1:0] rd_ptr_gray, rd_ptr_gray_next; + + // 2-flop synchronizers + // first is risky, second is much safer: 2 flip/flops prevent use of unsafe signals + logic [PTR_SIZE-1:0] rd_ptr_gray_sync [2]; + logic [PTR_SIZE-1:0] wr_ptr_gray_sync [2]; + + // Write Domain + + // next write pointer, if write signal high and isn't full + assign wr_ptr_bin_next = (wr_en_i && !full_o) ? (wr_ptr_bin + 1'b1) : wr_ptr_bin; + + assign wr_ptr_gray_next = wr_ptr_bin_next ^ (wr_ptr_bin_next >> 1); // binary -> gray conversion + + always_ff @(posedge wr_clk_i or negedge rst_ni) begin + if (!rst_ni) begin // reset logic + wr_ptr_bin <= '0; + wr_ptr_gray <= '0; + end else begin + wr_ptr_bin <= wr_ptr_bin_next; + wr_ptr_gray <= wr_ptr_gray_next; // pointer progression + + if (wr_en_i && !full_o) + mem[wr_ptr_bin[PTR_WIDTH-1:0]] <= wr_data_i; // if write signal high and isn't full, write memory + end + end + + // Synchronize read pointer to write clock + always_ff @(posedge wr_clk_i or negedge rst_ni) begin + if (!rst_ni) begin // reset logic + rd_ptr_gray_sync[0] <= '0; + rd_ptr_gray_sync[1] <= '0; + end else begin + rd_ptr_gray_sync[0] <= rd_ptr_gray; + rd_ptr_gray_sync[1] <= rd_ptr_gray_sync[0]; // two flip flop against metastability + end + end + + // full check + always_ff @(posedge wr_clk_i or negedge rst_ni) begin + if (!rst_ni) begin + full_o <= 1'b0; // reset logic: not full + end + // if write pointer has wrapped around, there is no room left + // lower bits equal, upper two bits inverted: write is one cycle ahead of read: full + else full_o <= (wr_ptr_gray_next[PTR_SIZE-1] != rd_ptr_gray_sync[1][PTR_SIZE-1]) && + (wr_ptr_gray_next[PTR_SIZE-2] != rd_ptr_gray_sync[1][PTR_SIZE-2]) && + (wr_ptr_gray_next[PTR_SIZE-3:0] == rd_ptr_gray_sync[1][PTR_SIZE-3:0]); + end + + // Read Domain + + // next read pointer, if read signal is high and isn't empty + assign rd_ptr_bin_next = (rd_en_i && !empty_o) ? (rd_ptr_bin + 1'b1) : rd_ptr_bin; + + assign rd_ptr_gray_next = rd_ptr_bin_next ^ (rd_ptr_bin_next >> 1); // binary -> gray conversion + + always_ff @(posedge rd_clk_i or negedge rst_ni) begin + if (!rst_ni) begin // reset logic + rd_ptr_bin <= '0; + rd_ptr_gray <= '0; + rd_data_o <= '0; + end else begin + rd_ptr_bin <= rd_ptr_bin_next; + rd_ptr_gray <= rd_ptr_gray_next; // pointer progression + + if (rd_en_i && !empty_o) // if read signal high and isn't empty, read memory + rd_data_o <= mem[rd_ptr_bin[PTR_WIDTH-1:0]]; + end + end + + // synchronize write pointer to read clock + always_ff @(posedge rd_clk_i or negedge rst_ni) begin + if (!rst_ni) begin // reset logic + wr_ptr_gray_sync[0] <= '0; + wr_ptr_gray_sync[1] <= '0; + end else begin + wr_ptr_gray_sync[0] <= wr_ptr_gray; + wr_ptr_gray_sync[1] <= wr_ptr_gray_sync[0]; // two flip flop against metastability + end + end + + // empty check + always_ff @(posedge rd_clk_i or negedge rst_ni) begin + if (!rst_ni) + empty_o <= 1'b1; // reset logic: empty + else empty_o <= (rd_ptr_gray_next == wr_ptr_gray_sync[1]); // read, write synced: empty + end + +endmodule diff --git a/rtl/bus/axi/axi_icache_port.sv b/rtl/bus/axi/axi_icache_port.sv index 1f92419..589d224 100644 --- a/rtl/bus/axi/axi_icache_port.sv +++ b/rtl/bus/axi/axi_icache_port.sv @@ -1,14 +1,87 @@ +// import interconnect_pkg; + module axi_icache_port #( - // TODO: Parameter setups - parameter + + // System AXI Parameters + parameter int unsigned AXI_ADDR_WIDTH = 32, + parameter int unsigned AXI_DATA_WIDTH = 64, + parameter int unsigned AXI_ID_WIDTH = 4, + parameter int unsigned AXI_USER_WIDTH = 1, + + )( - // TODO: Port set up - ports - // TODO: Input logic + // global clock and reset signals input logic clk_i, input logic rst_ni + + + // icache valid request and address + input logic ic_req_valid_i, + input logic[AXI_ADDR_WIDTH-1:0] ic_addr_valid_i, + + // I$ miss logic + input logic icache_miss_ar, + input logic icache_miss, + + + // axi address read signals + output logic [AXI_ADDR_WIDTH-1:0] axi_mem_ar_o, + output logic axi_ar_valid_o, + input logic axi_ar_ready_i, + output logic [AXI_ADDR_WIDTH-1:0] ar_addr, + + // axi read data signals + // TODO: figure out more needed parameters + input logic axi_mem_r_i, + input logic axi_r_ready_i, + input logic axi_r_valid_i, + input logic [AXI_ADDR_WIDTH*4-1:0] axi_mem_r_i, + input logic + + typedef enum [2:0] { + IDLE, // Do nothing, wait for icache to miss + AR_SEND, // cache requested a line, put it on araddr and set ARVALID to high + R_COLLECT, // collect the requested data from the crossbar and send it to cache, once rlast is recieved, flip back to idle + + } icache_port_state_e; + + icache_port_state_e current_state, next_state; + + // state transition + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + current_state <= IDLE; + end + else begin + current_state <= next_state; + end + end + + + // state transition logic + // TODO: finish the state transition logic + always_comb begin + next_state = current_state; + case (current_state) + IDLE: begin + if (icache_miss && ) next_state = AR_SEND; + + end + + AR_SEND: begin + if () next_state = R_COLLECT; + + end + + R_COLLECT: begin + if () next_state = IDLE; + + end + endcase + + end ); endmodule diff --git a/rtl/cpu/core/division.sv b/rtl/cpu/core/division.sv deleted file mode 100644 index 7eca5b4..0000000 --- a/rtl/cpu/core/division.sv +++ /dev/null @@ -1,174 +0,0 @@ - -import rv32_pkg::*; - -typedef enum {IDLE,STALL} state_t; - -typedef enum {GREATER_EQUAL_THAN_ZERO,LESS_THAN_ZERO} Test_remainder_flag_t; - -// Restoring division, divisor initially left-shifted by 32, quotient built MSB→LSB, divisor shifted right each iteration." - -module division - -( - input logic clk_i, - input logic rst_ni, - input logic Division_START, // Start Division - input logic [31:0] dividend, // numerator - input logic [31:0] divisor, // denominator - output logic [63:0] remainder, // Mod - output logic Division_DONE, // done flag - output logic [31:0] quotient, // result - output logic stall_o // outputting to stall the pipeline when high - ); - - logic [31:0] dividend_reg; // internal registers driven by inputs - logic signed [63:0] divisor_reg; - logic signed [63:0] remainder_reg; - logic [31:0] quotient_reg; - logic [5:0] counter; - logic Division_DONE_reg; - - state_t current_state, next_state; // Division FSM states 0 or 1 - - Test_remainder_flag_t Test_remainder_flag; // Test remainder 0 or 1 state - - assign stall_o = current_state; // used for stalling the pipeline - - - always_ff @(posedge clk_i or negedge rst_ni) begin // Division Stall FSM - if(rst_ni == 1'b0) begin // Active low reset - current_state <= IDLE; - end else begin - current_state <= next_state; - end - - end - - always_comb begin // Combinational part of state FSM - next_state = current_state; - - unique case(current_state) - IDLE:next_state = (Division_START) ? STALL : IDLE; // When Division_start gets asserted by mux, go to STALL - STALL: next_state = (Division_DONE_reg) ? IDLE : STALL; // When Division circuit is done on last clk edge, it will assert Division_DONE 1 to indicate DONE STATE - default: next_state = current_state; - endcase - - end - - - // Test combinational logic interface that will be used for testing. - logic signed [63:0] remainder_test_comparison; - logic [63:0] remainder_test_mux; - logic [63:0] divisor_comb; - logic [31:0] quotient_comb; - - assign remainder_test_comparison = remainder_reg - divisor_reg; // will subtract remainder from divisor - - assign Test_remainder_flag = (remainder_test_comparison[63]) ? LESS_THAN_ZERO : GREATER_EQUAL_THAN_ZERO; // will assign a flag based on the remainder - - - always_comb begin - // Restoring division step: - // Try subtracting divisor from remainder. - // If result >= 0 keep subtraction and append 1 to quotient. - // If result < 0 restore old remainder and append 0 to quotient. - // Then shift divisor for next bit position. - - - case(Test_remainder_flag) // Comparison case case - GREATER_EQUAL_THAN_ZERO: begin - remainder_test_mux = remainder_test_comparison; // Accept trial remainder (subtract succeeded) - quotient_comb = (quotient_reg << 1) | 1'b1; // shift left, add quotient bit = 1 - divisor_comb = divisor_reg >> 1; // next alignment (shift divisor) we are basically doing long division and comparing bit positions to see if they match - end - LESS_THAN_ZERO: begin - quotient_comb = (quotient_reg << 1) | 1'b0; // Restore remainder (subtract failed) - remainder_test_mux = remainder_reg; // Shift left, add quotient bit = 0 - divisor_comb = divisor_reg >> 1; // Next alignment (shift divisor) - end - default: begin // default should never happen but keeps tools happy - quotient_comb = (quotient_reg << 1) | 1'b0; - remainder_test_mux = remainder_reg; // Restore remainder (subtract failed) - divisor_comb = divisor_reg >> 1;// next alignment (shift divisor) we are basically doing long division and comparing bit positions to see if they match - - end - endcase - - end - - - always_ff @(posedge clk_i or negedge rst_ni) begin - if(rst_ni == 1'b0) begin - dividend_reg <= 32'd0; - divisor_reg <= 64'd0; - remainder_reg <= 64'd0; - quotient_reg <= 32'd0; - Division_DONE_reg <= 1'b0; - counter <= 6'd0; - - - end else begin - case(current_state) - IDLE: begin - - dividend_reg <= dividend; // dividend_reg will latch on to dividend input - divisor_reg <= divisor << 32; // Divisor is aligned to the top 32 bits, divisor_reg is 64 Bit reg - remainder_reg <= {32'd0,dividend}; // remainder_reg will be initialized with numerator - Division_DONE_reg <= 1'b0; - counter <= 6'd0; - quotient_reg <= 32'd0; - - end - STALL: begin - remainder_reg <= remainder_test_mux; - quotient_reg <= quotient_comb; - divisor_reg <= divisor_comb; - counter <= counter + 1'b1; - - if(counter == 6'd32) begin - Division_DONE_reg <= 1'b1; - - end else begin - - Division_DONE_reg <= 1'b0; - - end - - - end - - endcase - - end - - end - - always_ff @(posedge clk_i) begin - - if(counter == 32'd33) begin - $display("Quotient: %0d",quotient); - $display("Remainder: %0d",remainder); - $display("Counter: %0d",counter); - $display("Done Signal: %0d", Division_DONE); - $display("Current State: %s", current_state); - $display("next State: %s", next_state); - $display("Division Start flag: %d", Division_START); - $display("\n"); - end - - - - - - - end - - - assign remainder = remainder_reg; - assign quotient = quotient_reg; - assign Division_DONE = Division_DONE_reg; - - - - -endmodule \ No newline at end of file diff --git a/rtl/cpu/core/execute.sv b/rtl/cpu/core/execute.sv index 800ba91..e69de29 100644 --- a/rtl/cpu/core/execute.sv +++ b/rtl/cpu/core/execute.sv @@ -1,251 +0,0 @@ - - -/* Assumptions: - -1) All immediates are sign-extended, no zero extension AT ALL unless the ISA specifies it for special instructions -2) No overflow detection at the hardware level, we have to check at the software level -3) No integer computational instructions cause arithmetic exceptions, No overflow, underflow, carryout, or trap ever -4) unsigned instructions still get signed extended and instructions like SLTU will compare raw bit pattern, so SLTU -1 > 32 is TRUE for example, and not interpret the sign bit unlike SLT which is signed -5) divide (div), divide unsigned (divu), remainder (rem), and remainder unsigned (remu), see pg 390 -6) multiply (mul), multiply high(mulh), multiply high unsigned(mulhu) multiply high signed-unsigned(mulhsu) are supported 384 -*/ - -import rv32_pkg::*; - -module execute - -( - input logic clk_i, // Main clk input - input logic rst_ni, // Active-low asynchronous reset - input logic ctrl_i, // Control signals to execute - input logic [DATA_WIDTH-1:0] op_a_i, // Register A operand (data) from RF - input logic [DATA_WIDTH-1:0] op_b_i, // Register B operand (data) or sign extended immediate 32'b values - input logic [4:0] ALU_OP, - output logic [DATA_WIDTH-1:0] alu_res_o, // ALU result from procesing operands - output logic branch_taken_o, // Control signal for whether branch should be taken - output logic stall_o // used for stalling the pipeline when division module enters STALL state - //output logic [DATA_WIDTH-1:0] branch_target_o // Address for branch to redirect program counter deleted because decode will produce branch target in a registered state -); - - - // TEMP parameters - parameter FUNCT7_MUL = 5'd13; - parameter FUNCT7_MULH = 5'd14; - parameter FUNCT7_MULHSU = 5'd15; - parameter FUNCT_MULHU = 5'd16; - parameter FUNCT_XOR = 5'd17; - parameter FUNCT_BEQ = 5'd18; - parameter FUNCT_BNE = 5'd19; - parameter FUNCT_BLT = 5'd20; - parameter FUNCT_BGE = 5'd21; - parameter FUNCT_BLTU = 5'd22; - parameter FUNCT_BGEU = 5'd23; - parameter FUNCT_DIVU = 5'd24; - parameter FUNCT_REMU = 5'd25; - parameter FUNCT_DIV = 5'd26; - parameter FUNCT_REM = 5'd27; - - logic [DATA_WIDTH*2:0] MULTIPLY_REG; // Used for M instructions - logic [DATA_WIDTH-1:0] ALU_OUTPUT_COMB; - - - always_ff @(posedge ctrl_i or negedge rst_ni) begin - if(rst_ni == 1'b0) - alu_res_o <= 32'd0; - else begin - alu_res_o <= ALU_OUTPUT_COMB; - end - - end - - // Division interface - logic Division_START; - logic Division_DONE; - logic [DATA_WIDTH-1:0] divisor; // numberator - logic [DATA_WIDTH-1:0] dividend; // denominator - logic [DATA_WIDTH-1:0] quotient; //result - logic [DATA_WIDTH-1:0] result_fix; // Will decide if quotient should be signed - logic [DATA_WIDTH-1:0] remainder; // mod - logic sign_bit; // used for fixing sign bit in division - logic signed_overflow; - - division Unit( - .clk_i(clk_i), - .rst_ni(rst_ni), - .Division_START(Division_START), - .dividend(dividend), - .divisor(divisor), - .remainder(remainder), - .Division_DONE(Division_DONE), - .quotient(quotient), - .stall_o(stall_o) - ); - - - always_comb begin // ALU selection OPCODE - ALU_OUTPUT_COMB = 32'b0; // Defaults - MULTIPLY_REG = 64'b0; - branch_taken_o = 1'b0; - divisor = 32'b0; - dividend = 32'b0; - Division_START = 1'b0; - sign_bit = 1'b0; - result_fix = 32'b0; - signed_overflow = 1'b0; - - case(ALU_OP) - - // Assumption that all Immediate versions of the instructions will be included in op_b_i sign extended already - // For shamt instructions, I need only the raw 5 bit unsigned shamt value, no zero extension or zero padding. see how decoder is interfacing to me - FUNCT3_ADD: ALU_OUTPUT_COMB = op_a_i + op_b_i; - - FUNCT3_SUB: ALU_OUTPUT_COMB = $signed(op_a_i) - $signed(op_b_i); - - FUNCT3_SLL: ALU_OUTPUT_COMB = op_a_i << op_b_i; - - FUNCT3_SLT: ALU_OUTPUT_COMB = ($signed(op_a_i) < $signed(op_b_i)) ? 32'd1 : 32'd0; // if A < b return 1 else 0 SIGNED - - FUNCT3_SLTU: ALU_OUTPUT_COMB = (op_a_i < op_b_i) ? 32'd1 : 32'd0; // if A < b return 1 else 0 UNSIGNED - - FUNCT3_XOR: ALU_OUTPUT_COMB = op_a_i ^ op_b_i; // will XOR every individual bit - - FUNCT3_SRL: ALU_OUTPUT_COMB = op_a_i >> op_b_i; - - FUNCT3_OR: ALU_OUTPUT_COMB = op_a_i | op_b_i; - - FUNCT3_AND: ALU_OUTPUT_COMB = op_a_i & op_b_i; - - FUNCT7_SRA: ALU_OUTPUT_COMB = op_a_i >>> op_b_i; - - FUNCT_XOR: ALU_OUTPUT_COMB = op_a_i ^ op_b_i; - - FUNCT_BEQ: branch_taken_o = ($signed(op_a_i) == $signed(op_b_i)) ? 32'd1 : 32'd0; - - FUNCT_BNE: branch_taken_o = ($signed(op_a_i) != $signed(op_b_i)) ? 32'd1 : 32'd0; - - FUNCT_BLT: branch_taken_o = ($signed(op_a_i) < $signed(op_b_i)) ? 32'd1 : 32'd0; - - FUNCT_BLTU: branch_taken_o = (op_a_i < op_b_i) ? 32'd1 : 32'd0; - - FUNCT_BGE: branch_taken_o = ($signed(op_a_i) >= $signed(op_b_i)) ? 32'd1 : 32'd0; - - FUNCT_BGEU: branch_taken_o = (op_a_i >= op_b_i) ? 32'd1 : 32'd0; - - FUNCT7_MUL: begin // Will return lower XLEN x XLEN bits in ALU_OUTPUT, same for signed/unsigned XLEN - MULTIPLY_REG = op_a_i * op_b_i; - ALU_OUTPUT_COMB = MULTIPLY_REG[31:0]; - end - - FUNCT7_MULH: begin - MULTIPLY_REG = $signed(op_a_i) * $signed(op_b_i); // Will return upper signed(XLEN) x signed(XLEN) bits in ALU_OUTPUT - ALU_OUTPUT_COMB = MULTIPLY_REG[63:32]; - - end - - FUNCT7_MULHSU: begin // Will return Signed(XLEN) x Unsigned(XLEN) upper bits - MULTIPLY_REG = $signed(op_a_i) * (op_b_i); // In the RISC-V spec, rs2 is multiplier, rs1 is multiplicand, im assuming rs1 is op_a_i and op_b_i is multiplicand - ALU_OUTPUT_COMB = MULTIPLY_REG[63:32]; - end - - FUNCT_MULHU: begin // will return return unsigned x unsigned upper XLEN bits - MULTIPLY_REG = op_a_i * op_b_i; - ALU_OUTPUT_COMB = MULTIPLY_REG[63:32]; - - end - - FUNCT_DIVU: begin - dividend = op_a_i; - divisor = op_b_i; - Division_START = (op_b_i != 32'd0) ? 1'b1 : 1'b0; // if no division by zero start division - ALU_OUTPUT_COMB = (op_b_i != 32'd0) ? quotient : 32'hFFFF_FFFF; // zero edge case ALU will output all ones - - end - - FUNCT_REMU: begin - dividend = op_a_i; - divisor = op_b_i; - Division_START = (op_b_i != 32'd0) ? 1'b1 : 1'b0; // if no division by zero start division - ALU_OUTPUT_COMB = (op_b_i != 32'd0) ? remainder : op_a_i; // zero edge case ALU will output dividend - - end - FUNCT_REM: begin - - signed_overflow = (op_a_i == 32'h8000_0000) && (op_b_i == 32'hFFFF_FFFF); // signed overflow will only occur with -1 and -2^31 - - dividend = (op_a_i[31] == 1'b1) ? ~op_a_i + 1'b1 : op_a_i; // convert to unsigned magnitude if negative - - divisor = (op_b_i[31] == 1'b1) ? ~op_b_i + 1'b1 : op_b_i; // convert to unsigned magnitude if negative - - Division_START = (op_b_i != 32'd0 && signed_overflow == 1'b0) ? 1'b1 : 1'b0; // start Division if no signed overflow or division by zero - - sign_bit = op_a_i[31]; // For remiander, the sign follows the dividend only - - result_fix = (sign_bit == 1'b1) ? ~remainder + 1'b1 : remainder; // sign fixing logic that will fix unsigned remainder coming out of divider - - if(op_b_i == 32'd0) begin - - ALU_OUTPUT_COMB = op_a_i; // ALU output stays the dividend if div by 0 - - end else if(signed_overflow) begin - - ALU_OUTPUT_COMB = 32'b0; // ALU will output 32'b0 if signed overflow - - end else begin - - ALU_OUTPUT_COMB = result_fix; // if no signed overflow or div by 0 assign remainder - - end - - end - - FUNCT_DIV: begin - - signed_overflow = (op_a_i == 32'h8000_0000) && (op_b_i == 32'hFFFF_FFFF); // signed overflow will only occur with -1 and -2^31 - - dividend = (op_a_i[31] == 1'b1) ? ~op_a_i + 1'b1 : op_a_i; // convert to unsigned magnitude if negative - - divisor = (op_b_i[31] == 1'b1) ? ~op_b_i + 1'b1 : op_b_i; // convert to unsigned magnitude if negative - - Division_START = (op_b_i != 32'd0 && signed_overflow == 1'b0) ? 1'b1 : 1'b0; // start Division if no signed overflow or division by zero - - sign_bit = op_a_i[31] ^ op_b_i[31]; // check sign of dividend and divisor see if quotient needs to be fixed - - result_fix = (sign_bit == 1'b1) ? ~quotient + 1'b1 : quotient; // sign fixing logic that will fix unsigned quotient coming out of divider - - if(op_b_i == 32'd0) begin // signed overflow and div by 0 cases - - ALU_OUTPUT_COMB = 32'hFFFF_FFFF; //ALU output stays -1 if div by zero - - end else if(signed_overflow) begin - - ALU_OUTPUT_COMB = 32'h8000_0000; //ALU output stays -2^31 if signed overflow - - end else begin - - ALU_OUTPUT_COMB = result_fix; // if no overflow or div by zero then ALU will be the result)fix - - end - - end - - default: begin - ALU_OUTPUT_COMB = 32'b0; // Defaults - MULTIPLY_REG = 64'b0; - branch_taken_o = 1'b0; - divisor = 32'b0; - dividend = 32'b0; - Division_START = 1'b0; - sign_bit = 1'b0; - result_fix = 32'b0; - signed_overflow = 1'b0; - end - - - endcase - - - end - - - -endmodule diff --git a/rtl/cpu/core/fetch.sv b/rtl/cpu/core/fetch.sv index 2070b65..e69de29 100644 --- a/rtl/cpu/core/fetch.sv +++ b/rtl/cpu/core/fetch.sv @@ -1 +0,0 @@ -// Test \ No newline at end of file diff --git a/rtl/irq/clint.sv b/rtl/irq/clint.sv index 0665034..4e55cf2 100644 --- a/rtl/irq/clint.sv +++ b/rtl/irq/clint.sv @@ -1,43 +1,25 @@ module clint( input logic clk_i, input logic rst_ni, - // write ports - input logic msip_we, - input logic msip_wdata, - - input logic mtimecmp_we, - input logic [63:0] mtimecmp_wdata, - - // outputs - output logic [63:0] mtime_o, - output logic timer_irq_o, - output logic soft_irq_o - ); - - //---------------------------------------- - // Internal Registers - //---------------------------------------- + output logic Timer_irq_o, + input logic [63:0] user_time, // + output logic [63:0] mtime_o, // + output logic msip +); logic [63:0] mtimecmp; - logic msip; - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - mtime_o <= 64'd0; - mtimecmp <= 64'd0; - msip <= 1'b0; - timer_irq_o <= 1'b0; - soft_irq_o <= 1'b0; - end else begin - //free running timer - mtime_o <= mtime_o + 1; - - // register writes - if (mtimecmp_we) mtimecmp <= mtimecmp_wdata; - if (msip_we) msip <= msip_wdata; - - // irq outputs (compare against "next" mtime to avoid 1-cycle lag) - timer_irq_o <= ((mtime_o + 64'd1) >= (mtimecmp_we ? mtimecmp_wdata : mtimecmp)); - soft_irq_o <= (msip_we ? msip_wdata : msip); - end - end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + mtime_o <= 16'd0; + mtimecmp <= 16'd0; + Timer_irq_o <= 1'b0; + msip <= 1'b0; + end else begin + mtime_o <= mtime_o + 1; + mtimecmp <= user_time; + Timer_irq_o <= (mtime_o >= mtimecmp); + msip <= Timer_irq_o; + end + end endmodule + diff --git a/rtl/irq/plic.sv b/rtl/irq/plic.sv index a2582a2..e5be350 100644 --- a/rtl/irq/plic.sv +++ b/rtl/irq/plic.sv @@ -1,23 +1,21 @@ -`timescale 1ns / 1ps - -module PLIC #( - parameter NSOURCES = 8, - parameter PRIO_WIDTH = 3, - parameter SRC_ID_WIDTH = 3 +module plic #( + parameter NSOURCES = 32, + parameter PRIO_WIDTH = 3 )( - input logic clk_i, - input logic rst_ni, - input logic claim_req_i, - input logic complete_i, - input logic [NSOURCES-1:0] src_i, // External interrupt sources + input logic clk_i, + input logic rst_ni, + input logic [NSOURCES-1:0] src_i, // External interrupt sources - input logic [NSOURCES*PRIO_WIDTH-1:0] priority_wdata, - input logic priority_we, - input logic [NSOURCES-1:0] enable_wdata, - input logic enable_we, + // Simple register interface (bus-free) + input logic [NSOURCES*PRIO_WIDTH-1:0] priority_wdata, + input logic priority_we, + input logic [NSOURCES-1:0] enable_wdata, + input logic enable_we, + input logic [$clog2(NSOURCES)-1:0] claim_wdata, + input logic claim_we, output logic ext_irq_o, - output logic [SRC_ID_WIDTH-1:0] claim_o + output logic [$clog2(NSOURCES)-1:0] claim_o ); // ------------------------- @@ -25,69 +23,83 @@ module PLIC #( // ------------------------- logic [NSOURCES*PRIO_WIDTH-1:0] priorities; // Interrupt priorities logic [NSOURCES-1:0] enable; // Enable bits for sources - logic [SRC_ID_WIDTH-1:0] claim; // Claim register - logic [NSOURCES-1:0] pending; // Pending interrupts - logic [SRC_ID_WIDTH-1:0] highestPriorIndex; + logic [$clog2(NSOURCES)-1:0] claim; // Claim register + logic [NSOURCES-1:0] pending; // Pending interrupts + logic [$clog2(NSOURCES)-1:0] highestPriorIndex; logic [PRIO_WIDTH-1:0] tempHighestValue; logic activeClaim; - assign claim_o = activeClaim ? (claim + 1'b1) : '0; - assign ext_irq_o = (!activeClaim) && (tempHighestValue != 0); + assign claim_o = claim; // ------------------------- - // Priority selector + // Active low reset // ------------------------- - always_comb begin - tempHighestValue = 0; - highestPriorIndex = 0; - - for (int i = 0; i < NSOURCES; i++) begin - if (pending[i] && enable[i] && priorities[i*PRIO_WIDTH +: PRIO_WIDTH] > tempHighestValue) begin - tempHighestValue = priorities[i*PRIO_WIDTH +: PRIO_WIDTH]; - highestPriorIndex = i[SRC_ID_WIDTH-1:0]; - end + always_ff @(negedge rst_ni) begin + if (!rst_ni) begin + priorities <= 0; + enable <= 0; + claim <= 0; + pending <= 0; + highestPriorIndex <= 0; + tempHighestValue <= 0; + activeClaim <= 0; end end // ------------------------- - // Sequential logic + // Simple register writes // ------------------------- - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - priorities <= 0; - enable <= 0; - claim <= 0; - pending <= 0; - activeClaim <= 0; - end else begin - - // Register writes - if (priority_we) - priorities <= priority_wdata; + always_ff @(posedge clk_i) begin + if (priority_we) begin + priorities <= priority_wdata; + end + if (enable_we) begin + enable <= enable_wdata; + end + if (claim_we) begin + activeClaim <= 0; // Claim complete + end + end - if (enable_we) - enable <= enable_wdata; + // ------------------------- + // Pending interrupt latching + // ------------------------- + always_ff @(posedge clk_i) begin + for (int i = 0; i < NSOURCES; i++) begin + pending[i] <= pending[i] | (src_i[i] & enable[i]); + end + end - // Pending interrupt latching - for (int i = 0; i < NSOURCES; i++) begin - pending[i] <= pending[i]; - if(!(activeClaim && (i == claim))) begin - pending[i] <= pending[i] | src_i[i]; - end + // ------------------------- + // Priority selector + // ------------------------- + always_ff @(posedge clk_i) begin + tempHighestValue <= 0; + highestPriorIndex <= 0; + for (int i = 0; i < NSOURCES; i++) begin + if (pending[i] && priorities[i*PRIO_WIDTH +: PRIO_WIDTH] > tempHighestValue) begin + tempHighestValue <= priorities[i*PRIO_WIDTH +: PRIO_WIDTH]; + highestPriorIndex <= i[$clog2(NSOURCES)-1:0]; end + end + end - //Complete logic - if (complete_i && activeClaim) begin - activeClaim <= 0; - end - - // Claim logic - if (claim_req_i && !activeClaim && tempHighestValue != 0) begin - claim <= highestPriorIndex; - pending[highestPriorIndex] <= 0; - activeClaim <= 1; - end + // ------------------------- + // Claim logic + // ------------------------- + always_ff @(posedge clk_i) begin + if (!activeClaim && tempHighestValue != 0) begin + claim <= highestPriorIndex; + pending[highestPriorIndex] <= 0; + activeClaim <= 1; end end -endmodule + // ------------------------- + // IRQ output + // ------------------------- + always_ff @(posedge clk_i) begin + ext_irq_o <= activeClaim; + end + +endmodule diff --git a/sim/sv/example.sv b/sim/sv/example.sv deleted file mode 100644 index 8b13789..0000000 --- a/sim/sv/example.sv +++ /dev/null @@ -1 +0,0 @@ -