diff --git a/docs/ip-briefs/axi_crossbar.md b/docs/ip-briefs/axi_crossbar.md index 559da11..9fcf462 100644 --- a/docs/ip-briefs/axi_crossbar.md +++ b/docs/ip-briefs/axi_crossbar.md @@ -35,14 +35,14 @@ Central AXI4 interconnect between managers and subordinates. Performs address de ### Performance Targets -- **Clock:** **TBD** MHz (ASIC), **TBD** MHz (FPGA) +- **Clock:** 100 MHz (FPGA) - **Throughput:** **1 beat/cycle** once granted (R & W) - **Latency (xbar-only):** addr → grant ≤ 2 cycles; read addr → first data ≤ 3 cycles beyond subordinate; last-W → B ≤ 3 cycles - **Arbitration:** **Round-robin;** starvation ≤ **N_M grants** -- **Bursts:** **INCR**, max len **TBD** -- **Outstanding:** per-manager **TBD R/TBD W**; backpressure only -- **CDC:** each crossing adds **+TBD cycles;** throughput unchanged -- **Reset:** READY may assert within ≤ **TBD cycles** after `rst_ni` deassert +- **Bursts:** **INCR**, max len 16 +- **Outstanding:** per-manager 2 R/2 W; backpressure only +- **CDC:** each crossing adds 3-5 cycles; throughput unchanged +- **Reset:** READY may assert within ≤ 2 cycles after `rst_ni` deassert --- ### Behavior & Timing diff --git a/docs/ip-briefs/plic_module_brief_v0.2.md b/docs/ip-briefs/plic_module_brief_v0.2.md index 24f5a83..b0b4c65 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 (v0.3) +# PLIC - Module Brief (v1.0) **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 32 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 `NSOURCES` 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: 32 (`NSOURCES`) -- Priority field width per source: 3 bits (`PRIO_WIDTH`) -- Interrupt ID width for claim/complete operations: `$clog2(NSOURCES)` +- 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) ### Interfaces (Ports) @@ -18,52 +18,57 @@ 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 | -| src_i | in | 32 | External interrupt sources | -| priority_wdata | in | 96 | Data to write to all priority registers (32 × 3) | +| 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 | | priority_we | in | 1 | Write enable for priority registers | -| enable_wdata | in | 32 | Data to write to enable register | +| enable_wdata | in | NSOURCES | 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 | 5 | Current claimed interrupt ID | +| claim_o | out | SRC_ID_WIDTH | Current claimed interrupt ID (1-based, 0 = none) | ### 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`—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 state—are cleared to 0, and the output signal `ext_irq_o` is deasserted. ### Behavior and Timing -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. +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. ### Programming Model -The PLIC provides three sets of registers controlled via simple write-enable/data inputs: +The PLIC provides two 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. -All registers are accessible through the `_we` / `_wdata` inputs in this bus-free implementation. +Claim and completion are handled via `claim_req_i` and `complete_i` handshake signals. ### Errors/IRQs | **IRQ** | **Source** | **Trigger** | **Clear** | |------------|-----------|----------------------------------|-------------------------------------| -| ext_irq_o | src_i | One or more enabled interrupts pending | Cleared when CPU signals completion via claim input | +| 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 | The PLIC does not generate additional internal error signals; all interrupts come from external sources. ### Performance Targets -- `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. +- `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. ### 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. +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. ### 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/complete 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 operation, and that the module responds correctly to reset (`rst_ni`). diff --git a/gamingCPU.editorconfig b/gamingCPU.editorconfig new file mode 100644 index 0000000..12a5341 --- /dev/null +++ b/gamingCPU.editorconfig @@ -0,0 +1,17 @@ +root = true +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +[*.{sv,svh,v,vh}] +indent_style = space +indent_size = 2 +max_line_length = 100 +[*.{c,h}] +indent_style = space +indent_size = 2 +max_line_length = 100 +[*.{yaml,yml,dts,dtsi,md}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/rtl/bus/axi/axi_dcache_port.sv b/rtl/bus/axi/axi_dcache_port.sv index 2859d17..9ab8c90 100644 --- a/rtl/bus/axi/axi_dcache_port.sv +++ b/rtl/bus/axi/axi_dcache_port.sv @@ -1,6 +1,6 @@ module axi_dcache_port #( // TODO: Parameter setups - parameter + parameter unsigned )( // TODO: Port set up ports @@ -9,6 +9,7 @@ module axi_dcache_port #( input logic clk_i, input logic rst_ni + ); 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/bus/bridges/ahb_to_axi.sv b/rtl/bus/bridges/ahb_to_axi.sv index e69de29..23a4add 100644 --- a/rtl/bus/bridges/ahb_to_axi.sv +++ b/rtl/bus/bridges/ahb_to_axi.sv @@ -0,0 +1 @@ +//Divyesh Narra \ No newline at end of file diff --git a/rtl/bus/interconnect_pkg.sv b/rtl/bus/interconnect_pkg.sv index e69de29..71f8bce 100644 --- a/rtl/bus/interconnect_pkg.sv +++ b/rtl/bus/interconnect_pkg.sv @@ -0,0 +1,21 @@ +// + + +package interconnect_pkg; + +// AXI Data Widths +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; + +parameter int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH/8; + +// Master IDs (TBD) + +// Address Map (TBD) + +// Cache Line + + +endpackage \ No newline at end of file diff --git a/rtl/cpu/core/division.sv b/rtl/cpu/core/division.sv new file mode 100644 index 0000000..7eca5b4 --- /dev/null +++ b/rtl/cpu/core/division.sv @@ -0,0 +1,174 @@ + +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 e69de29..800ba91 100644 --- a/rtl/cpu/core/execute.sv +++ b/rtl/cpu/core/execute.sv @@ -0,0 +1,251 @@ + + +/* 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 e69de29..2070b65 100644 --- a/rtl/cpu/core/fetch.sv +++ b/rtl/cpu/core/fetch.sv @@ -0,0 +1 @@ +// Test \ No newline at end of file diff --git a/rtl/irq/clint.sv b/rtl/irq/clint.sv index 4e55cf2..0665034 100644 --- a/rtl/irq/clint.sv +++ b/rtl/irq/clint.sv @@ -1,25 +1,43 @@ module clint( input logic clk_i, input logic rst_ni, - output logic Timer_irq_o, - input logic [63:0] user_time, // - output logic [63:0] mtime_o, // - output logic msip -); + // 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 + //---------------------------------------- logic [63:0] mtimecmp; - - 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 + 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 endmodule - diff --git a/rtl/irq/plic.sv b/rtl/irq/plic.sv index e5be350..a2582a2 100644 --- a/rtl/irq/plic.sv +++ b/rtl/irq/plic.sv @@ -1,21 +1,23 @@ -module plic #( - parameter NSOURCES = 32, - parameter PRIO_WIDTH = 3 +`timescale 1ns / 1ps + +module PLIC #( + parameter NSOURCES = 8, + parameter PRIO_WIDTH = 3, + parameter SRC_ID_WIDTH = 3 )( - input logic clk_i, - input logic rst_ni, - input logic [NSOURCES-1:0] src_i, // External interrupt sources + 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 - // 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, + input logic [NSOURCES*PRIO_WIDTH-1:0] priority_wdata, + input logic priority_we, + input logic [NSOURCES-1:0] enable_wdata, + input logic enable_we, output logic ext_irq_o, - output logic [$clog2(NSOURCES)-1:0] claim_o + output logic [SRC_ID_WIDTH-1:0] claim_o ); // ------------------------- @@ -23,83 +25,69 @@ module plic #( // ------------------------- logic [NSOURCES*PRIO_WIDTH-1:0] priorities; // Interrupt priorities logic [NSOURCES-1:0] enable; // Enable bits for sources - logic [$clog2(NSOURCES)-1:0] claim; // Claim register - logic [NSOURCES-1:0] pending; // Pending interrupts - logic [$clog2(NSOURCES)-1:0] highestPriorIndex; + logic [SRC_ID_WIDTH-1:0] claim; // Claim register + logic [NSOURCES-1:0] pending; // Pending interrupts + logic [SRC_ID_WIDTH-1:0] highestPriorIndex; logic [PRIO_WIDTH-1:0] tempHighestValue; logic activeClaim; - assign claim_o = claim; + assign claim_o = activeClaim ? (claim + 1'b1) : '0; + assign ext_irq_o = (!activeClaim) && (tempHighestValue != 0); // ------------------------- - // Active low reset - // ------------------------- - 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 - - // ------------------------- - // Simple register writes + // Priority selector // ------------------------- - 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 + always_comb begin + tempHighestValue = 0; + highestPriorIndex = 0; - // ------------------------- - // 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]); + 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 end end // ------------------------- - // Priority selector + // Sequential logic // ------------------------- - 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]; + 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; + + if (enable_we) + enable <= enable_wdata; + + // 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 end - end - end - // ------------------------- - // Claim logic - // ------------------------- - always_ff @(posedge clk_i) begin - if (!activeClaim && tempHighestValue != 0) begin - claim <= highestPriorIndex; - pending[highestPriorIndex] <= 0; - activeClaim <= 1; + //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 end end - - // ------------------------- - // IRQ output - // ------------------------- - always_ff @(posedge clk_i) begin - ext_irq_o <= activeClaim; - end - endmodule +