diff --git a/docs/DMA_controller.md b/docs/DMA_controller.md new file mode 100644 index 0000000..b69fb3d --- /dev/null +++ b/docs/DMA_controller.md @@ -0,0 +1,80 @@ +# GamingCPU — DMA Controller + +**Owner:** Evan Eichholz | **RTL Target:** rtl/dma/dma_controller.sv | **Version:** v0. + +## Purpose & Role + +High-performance Direct Memory Access controller for autonomous data transfers between +memory and peripherals. Offloads bulk data movement from CPU, enabling concurrent +computation during video/audio/storage operations. + +## Design Specifications + +**AXI4 Master** - Full bus master for memory access **Multi-Channel** - 4 independent channels +with priority arbitration +**Scatter-Gather** - Linked list descriptor support **Burst Optimization** - Maximum bus +efficiency **Interrupt Driven** - Completion/error signaling + +## Implementation Tasks + +``` +Task Decision Deliverable +``` +``` +Channel Arbitration Fixed priority Bandwidth allocation +``` +``` +Descriptor Fetch Hardware managed Automatic chain loading +``` +``` +Burst Control Adaptive sizing Optimal bus utilization +``` +``` +Error Handling Abort + interrupt Transfer recovery +``` +``` +Register Interface AXI-Lite Control/status access +``` +## Channel Configuration + +``` +Channel Priority Use Case Burst Size +``` +``` +0 Highest Video Frame Buffer 16 beats +``` +``` +1 High Audio Sample Stream 8 beats +``` +``` +2 Medium SD Card Storage 4 beats +``` +``` +3 Low General Purpose Configurable +``` +# INTERFACE DEFINITION + +**Module:** dma_controller **Parameters:** CHANNELS = 4, DATA_W = 32 + +**Ports:** + +``` +clk_i, rst_ni - Clock and active-low reset +m_axi_awaddr[31:0] - AXI write address +m_axi_awlen[7:0] - AXI write burst length +m_axi_awvalid - AXI write address valid +``` + +m_axi_awready - AXI write address ready +m_axi_araddr[31:0] - AXI read address +m_axi_arlen[7:0] - AXI read burst length +m_axi_arvalid - AXI read address valid +m_axi_arready - AXI read address ready +s_axi_araddr[31:0] - Control read address +s_axi_arvalid - Control read valid +s_axi_arready - Control read ready +s_axi_awaddr[31:0] - Control write address +s_axi_awvalid - Control write valid +s_axi_awready - Control write ready +irq_done_o[CHANNELS-1:0] - Transfer complete interrupts +irq_error_o[CHANNELS-1:0] - Error interrupts diff --git a/docs/SRAM_dualport.md b/docs/SRAM_dualport.md new file mode 100644 index 0000000..4166060 --- /dev/null +++ b/docs/SRAM_dualport.md @@ -0,0 +1,70 @@ +# GamingCPU — SRAM Dualport + +**Owner:** Evan Eichholz **RTL Target:** rtl/mem/sram_dualport.sv +**Version:** v0. + +## Purpose & Role + +GamingCPU will implement true dual-port SRAM macro using FPGA block RAM (BRAM) +primitives. This block will serve as the fundamental building block for all shared, +concurrent-access memory structures in the SoC by creating a reliable, area-efficient +memory block that two independent clients can use simultaneously + +## Design + +**True Dual-Port** - Both ports operate simultaneously, no arbitration +**FPGA BRAM Mapping** - Use dedicated memory blocks, not flip-flops +**Byte-Level Writes** - Modify individual bytes without read-modify-write penalty +**Timing Closure** - Add output registers for better timing + +## Implementation Tools + +Creating a resource, area-efficient memory block that two independent clients can use +simultaneously + +## Design Specifications + +**True Dual-Port** - Both ports operate simultaneously, no arbitration +**FPGA BRAM Mapping** - Use dedicated memory blocks, not flip-flops +**Byte-Level Writes** - Modify individual bytes without read-modify-write penalty +**Timing Closure** - Add output registers for better timing + +# Implementation Tasks + +| **BRAM Instantiation** | Inference vs Direct | Vendor-agnostic RTL | **Port Wiring** | +Independent access | No coordination logic | **Byte Enables** | Native BRAM support | Per- +byte write capability | **Output Registers** | OUT_REG=0 vs 1 | Timing vs latency +trade-off | **Collision Handling** | Read-old-data vs read-new-data | Documented behavior + +# Integration + +| **RAM Handler** | Bus interface wrapper | AXI/TL-UL conversion | **Video Pipeline** | Line +buffers, framebuffers | Direct memory access | **Audio System** | Sample buffers | +Streaming data | **CPU/Coprocessors** | Shared data structures | Concurrent access + +# Interface + +``` +module sram_dualport #( +parameter int DATA_W = 32, +parameter int ADDR_W = 10, +parameter bit OUT_REG = 0 + + + )( + +input logic clk_i, +// Port A +input logic [ADDR_W-1:0] port_a_addr_i, +input logic [DATA_W-1:0] port_a_wdata_i, +input logic port_a_we_i, +input logic [(DATA_W/8)-1:0] port_a_be_i, +output logic [DATA_W-1:0] port_a_rdata_o, +// Port B +input logic [ADDR_W-1:0] port_b_addr_i, +input logic [DATA_W-1:0] port_b_wdata_i, +input logic port_b_we_i, +input logic [(DATA_W/8)-1:0] port_b_be_i, +output logic [DATA_W-1:0] port_b_rdata_o +); +``` diff --git a/docs/bootrom.md b/docs/bootrom.md new file mode 100644 index 0000000..ef708f0 --- /dev/null +++ b/docs/bootrom.md @@ -0,0 +1,41 @@ +# GamingCPU — Boot ROM + +``` +Owner: [Evan Eichholz] | RTL Target: rtl/mem/boot_rom.sv | Version: v0. +``` +## Purpose & Role + +``` +Initial boot code storage and CPU initialization. Contains the first instructions executed by +the processor after reset, handling early system setup, peripheral initialization, and FSBL +(First Stage Boot Loader) loading from storage. +``` +## Design Specifications + +``` +Read-Only Memory - Pre-programmed content, no write capability +Fixed Address Range - 4KB memory space at hardware-defined base address +Minimal Latency - Single-cycle read access for immediate execution +Synchronous Interface - Clocked read operations only +``` +## Implementation Tasks + +``` +Task Decision Deliverable +Memory Initialization $readmemh from hex file Pre-programmed boot code +Interface Design Simple read-only port Clean CPU interface +Address Decoding Full 4KB range No external decoding needed +Timing Optimization Combinational read Zero-wait-state access +``` +## INTERFACE DEFINITION + + + +``` +module boot_rom #( parameter int ADDR_W = 12, // 4KB address space +parameter int DATA_W = 32 // 32-bit instruction width +)( +input logic clk_i, input logic rst_ni, +input logic [ADDR_W-1:0] addr_i, +output logic [DATA_W-1:0] data_o, output logic valid_o +); diff --git a/rtl/dma/dma_channel_arbiter.sv b/rtl/dma/dma_channel_arbiter.sv index 646826d..e78f8f2 100644 --- a/rtl/dma/dma_channel_arbiter.sv +++ b/rtl/dma/dma_channel_arbiter.sv @@ -12,21 +12,36 @@ module dma_channel_arbiter import dma_pkg::*; ( ); logic [CHANNEL_W-1:0] current_channel; + logic [N_CHANNELS-1:0] grant_next; + // fixed priority logic + always_comb begin + grant_next = '0; + + // Channel 0 has highest priority + for (int i = 0; i < N_CHANNELS; i++) begin + if (req_i[i]) begin + grant_next[i] = 1'b1; + break; + end + end + end + + // reg outputs always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin current_channel <= '0; grant_o <= '0; - end else begin - // Fixed priority arbitration (Channel 0 highest priority) - grant_o <= '0; + end + else begin + grant_o <= grant_next; + + // track which channel got granted for (int i = 0; i < N_CHANNELS; i++) begin - if (req_i[i]) begin - grant_o[i] <= 1'b1; - break; - end + if (grant_next[i]) + current_channel <= i; end end end -endmodule \ No newline at end of file +endmodule diff --git a/rtl/dma/dma_controller.sv b/rtl/dma/dma_controller.sv index 39319e0..74d0e28 100644 --- a/rtl/dma/dma_controller.sv +++ b/rtl/dma/dma_controller.sv @@ -1,45 +1,93 @@ //====================================================================== // DMA Controller - Top Level -// Author: Evan Eichholz +// Author: Evan Eichholz // Description: Multi-channel DMA with scatter-gather support // References: specs/registers/dma.yaml, docs/dma_operation.md //====================================================================== -module dma_controller import dma_pkg::*; ( - // Clock and reset +module dma_controller + import dma_pkg::*; +( input logic clk_i, input logic rst_ni, - - // AXI4 Memory Interface (Master) - Write address channel + output logic [ADDR_W-1:0] m_axi_awaddr_o, output logic [7:0] m_axi_awlen_o, - // ... (other AXI ports - truncated for brevity) - - // AXI4-Lite Control Interface (Slave) + output logic [2:0] m_axi_awsize_o, + output logic [1:0] m_axi_awburst_o, + output logic m_axi_awvalid_o, + input logic m_axi_awready_i, + + output logic [DATA_W-1:0] m_axi_wdata_o, + output logic [DATA_W/8-1:0] m_axi_wstrb_o, + output logic m_axi_wlast_o, + output logic m_axi_wvalid_o, + input logic m_axi_wready_i, + + input logic [1:0] m_axi_bresp_i, + input logic m_axi_bvalid_i, + output logic m_axi_bready_o, + + output logic [ADDR_W-1:0] m_axi_araddr_o, + output logic [7:0] m_axi_arlen_o, + output logic [2:0] m_axi_arsize_o, + output logic [1:0] m_axi_arburst_o, + output logic m_axi_arvalid_o, + input logic m_axi_arready_i, + + input logic [DATA_W-1:0] m_axi_rdata_i, + input logic [1:0] m_axi_rresp_i, + input logic m_axi_rlast_i, + input logic m_axi_rvalid_i, + output logic m_axi_rready_o, + input logic [ADDR_W-1:0] s_axi_awaddr_i, - // ... (other AXI-Lite ports) - - // Interrupt outputs + input logic s_axi_awvalid_i, + output logic s_axi_awready_o, + + input logic [DATA_W-1:0] s_axi_wdata_i, + input logic [DATA_W/8-1:0] s_axi_wstrb_i, + input logic s_axi_wvalid_i, + output logic s_axi_wready_o, + + output logic [1:0] s_axi_bresp_o, + output logic s_axi_bvalid_o, + input logic s_axi_bready_i, + + input logic [ADDR_W-1:0] s_axi_araddr_i, + input logic s_axi_arvalid_i, + output logic s_axi_arready_o, + + output logic [DATA_W-1:0] s_axi_rdata_o, + output logic [1:0] s_axi_rresp_o, + output logic s_axi_rvalid_o, + input logic s_axi_rready_i, + output logic [N_CHANNELS-1:0] irq_done_o, output logic [N_CHANNELS-1:0] irq_error_o, - - // Peripheral request interface + input logic [N_CHANNELS-1:0] periph_req_i, output logic [N_CHANNELS-1:0] periph_ack_o ); - // Internal signals - dma_regs_t regs_q, regs_d; - channel_state_e [N_CHANNELS-1:0] channel_state; - logic [N_CHANNELS-1:0] channel_grant; + dma_regs_t regs; + logic [N_CHANNELS-1:0] ch_req; + logic [N_CHANNELS-1:0] ch_grant; + logic [CHANNEL_W-1:0] grant_ch; + logic grant_valid; + ch_status_t [N_CHANNELS-1:0] ch_status; + xfer_req_t xfer_req; + xfer_resp_t xfer_resp; + desc_fetch_req_t desc_req; + desc_fetch_resp_t desc_resp; + irq_type_e [N_CHANNELS-1:0] irq_type; - // Module instances + dma_reg_if u_reg_if (.*); dma_channel_arbiter u_channel_arbiter (.*); - dma_desc_fetch u_desc_fetch (.*); - dma_xfer_engine u_xfer_engine (.*); - dma_axi_mux u_axi_mux (.*); - dma_reg_if u_reg_if (.*); - dma_irq_ctrl u_irq_ctrl (.*); - dma_status u_status (.*); - -endmodule \ No newline at end of file + dma_desc_fetch u_desc_fetch (.*); + dma_xfer_engine u_xfer_engine (.*); + dma_axi_mux u_axi_mux (.*); + dma_irq_ctrl u_irq_ctrl (.*); + dma_status u_status (.*); + +endmodule diff --git a/rtl/dma/dma_pkg.sv b/rtl/dma/dma_pkg.sv index 720e3ca..81d4bea 100644 --- a/rtl/dma/dma_pkg.sv +++ b/rtl/dma/dma_pkg.sv @@ -45,4 +45,55 @@ package dma_pkg; logic [31:0] channel_enable; } dma_regs_t; -endpackage \ No newline at end of file + parameter logic [ADDR_W-1:0] REG_CTRL_OFFSET = 32'h0000_0000; + parameter logic [ADDR_W-1:0] REG_STATUS_OFFSET = 32'h0000_0004; + parameter logic [ADDR_W-1:0] REG_CHANNEL_ENABLE = 32'h0000_0008; + parameter logic [ADDR_W-1:0] REG_DESC_PTR_BASE = 32'h0000_0100; + parameter int CTRL_GLOBAL_ENABLE_BIT = 0; + parameter int CTRL_IRQ_ENABLE_BIT = 1; + parameter int STATUS_BUSY_BIT = 0; + parameter int STATUS_ERROR_BIT = 1; + + typedef enum logic [1:0] { + IRQ_NONE = 2'b00, + IRQ_DONE = 2'b01, + IRQ_ERROR = 2'b10 + } irq_type_e; + + typedef struct packed { + channel_state_e state; + logic done; + logic error; + } ch_status_t; + + typedef struct packed { + logic valid; + logic [ADDR_W-1:0] addr; + } desc_fetch_req_t; + typedef struct packed { + logic valid; + dma_desc_t desc; + } desc_fetch_resp_t; + + typedef struct packed { + logic valid; + logic [ADDR_W-1:0] src_addr; + logic [ADDR_W-1:0] dst_addr; + logic [23:0] length; + logic is_last; + } xfer_req_t; + typedef struct packed { + logic done; + logic error; + } xfer_resp_t; + + parameter int unsigned MAX_BURST_BEATS = 16; + parameter int unsigned MAX_BURST_BYTES = MAX_BURST_BEATS * (DATA_W/8); + + function automatic [7:0] calc_axi_len(input int unsigned bytes); + if (bytes > MAX_BURST_BYTES) + return MAX_BURST_BEATS - 1; + else + return (bytes / (DATA_W/8)) - 1; + endfunction +endpackage diff --git a/rtl/mem/boot_rom_tb.sv b/rtl/mem/boot_rom_tb.sv new file mode 100644 index 0000000..815b70d --- /dev/null +++ b/rtl/mem/boot_rom_tb.sv @@ -0,0 +1,57 @@ +//when the reset is active, data_o should just be all zeroes. Then every reset is inactive and now every 4th period (40 ns) it should first read +//@0000 at addr_i = 12h'000, +//0000006F at addr_i = 12h'004, +//00000013 at addr_i = 12h'008. + +`timescale 1ns / 1ps + +module boot_rom_tb(); + localparam ADDR_W = 12; + localparam DATA_W = 32; + + logic clk_i, rst_ni, valid_o; + logic [ADDR_W-1: 0] addr_i; + logic [DATA_W-1:0] data_o; + + boot_rom UTT(.clk_i(clk_i), .rst_ni(rst_ni), .valid_o(valid_o), .addr_i(addr_i), .data_o(data_o)); + + initial begin + forever #10 clk_i = ~clk_i; + end + + initial begin + rst_ni = 0; + addr_i = 0; + + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + + rst_ni = 1; + + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + + addr_i = 12'h000; + + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + + addr_i = 12'h004; + + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + @(posedge clk_i); + + addr_i = 12'h008; + + end + + +endmodule diff --git a/rtl/mem/bootrom.sv b/rtl/mem/bootrom.sv index e69de29..f62f680 100644 --- a/rtl/mem/bootrom.sv +++ b/rtl/mem/bootrom.sv @@ -0,0 +1,34 @@ +module boot_rom #( + parameter ADDR_W = 12, + parameter DATA_W = 32 +)( +input logic clk_i, +input logic rst_ni, + input logic [ADDR_W-1:0] addr_i, + output logic [DATA_W-1:0] data_o, + output logic valid_o +); + + logic [DATA_W-1:0] rom [0:2**ADDR_W-1]; + + initial begin + $readmemh("boot_code.mem", rom); + end + +logic [DATA_W-1:0] data_q; +logic valid_q; + + always_ff @(posedge clk_i) begin + if (!rst_ni) begin + data_q <= '0; + valid_q <= 1'b0; + end else begin + data_q <= rom[addr_i[ADDR_W-1: 2]]; + valid_q <= 1'b1; + end + end + +assign data_o = data_q; +assign valid_o = valid_q; + +endmodule diff --git a/rtl/mem/sram_dualport.sv b/rtl/mem/sram_dualport.sv index e69de29..9088e30 100644 --- a/rtl/mem/sram_dualport.sv +++ b/rtl/mem/sram_dualport.sv @@ -0,0 +1,54 @@ +module sram_dualport #( + parameter int DATA_W = 32, + parameter int ADDR_W = 10, + parameter bit OUT_REG = 0 +) ( + input logic clk_i, + // Port A + input logic [ADDR_W-1:0] port_a_addr_i, // port a address input + input logic [DATA_W-1:0] port_a_wdata_i, // port a write data + input logic port_a_we_i, // port a write enable input, 1 = write, 0 = read + input logic [(DATA_W/8)-1:0] port_a_be_i, // port a byte write enable input, corresponds to which bytes in mem to be overwritten + output logic [DATA_W-1:0] port_a_rdata_o, // read data output + // Port B + input logic [ADDR_W-1:0] port_b_addr_i, // port b address input + input logic [DATA_W-1:0] port_b_wdata_i, // port b write data + input logic port_b_we_i, // port b write enable input, 1 = write, 0 = read + input logic [(DATA_W/8)-1:0] port_b_be_i, // port b byte write enable input, corresponds to which bytes in mem to be overwritten + output logic [DATA_W-1:0] port_b_rdata_o // read data output +); + + // Shared memory array + logic [DATA_W-1:0] mem_a [0:(1<