From bf08f65279efa9e3224b8fe3ec1da8b86738a3bf Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Tue, 2 Sep 2025 14:08:16 +0100 Subject: [PATCH 1/4] Introduce a separate parameter for the tag RAM size Parameterise the tagged portion of the HyperRAM so that it is possible to map the entire HyperRAM without exhausting the block RAMs of the A50T. Attempting to store a capability to the untagged region shall return a bus error to the LSU. --- data/xbar_ifetch.hjson | 2 +- data/xbar_main.hjson | 2 +- data/xbar_main.hjson.tpl | 2 +- rtl/bus/tl_ifetch_pkg.sv | 2 +- rtl/bus/tl_main_pkg.sv | 2 +- rtl/ip/hyperram/rtl/hbmc_tl_port.sv | 167 +++++++++++++++++----------- rtl/ip/hyperram/rtl/hbmc_tl_top.sv | 32 +++--- rtl/ip/hyperram/rtl/hyperram.sv | 12 +- rtl/system/sonata_system.sv | 30 ++--- 9 files changed, 149 insertions(+), 102 deletions(-) diff --git a/data/xbar_ifetch.hjson b/data/xbar_ifetch.hjson index cbff89542..dad16b40e 100644 --- a/data/xbar_ifetch.hjson +++ b/data/xbar_ifetch.hjson @@ -37,7 +37,7 @@ xbar: false, addr_range: [{ base_addr: "0x40000000", - size_byte: "0x00100000", + size_byte: "0x00800000", }], }, { name: "dbg_dev", // Debug module fetch interface diff --git a/data/xbar_main.hjson b/data/xbar_main.hjson index a2c0f1dc5..a473377ce 100644 --- a/data/xbar_main.hjson +++ b/data/xbar_main.hjson @@ -48,7 +48,7 @@ xbar: false, addr_range: [{ base_addr: "0x40000000", - size_byte: "0x00100000", + size_byte: "0x00800000", }], }, { name: "rev_tag", // Revocation tag memory diff --git a/data/xbar_main.hjson.tpl b/data/xbar_main.hjson.tpl index 896b6311f..c967a6488 100644 --- a/data/xbar_main.hjson.tpl +++ b/data/xbar_main.hjson.tpl @@ -48,7 +48,7 @@ xbar: false, addr_range: [{ base_addr: "0x40000000", - size_byte: "0x00100000", + size_byte: "0x00800000", }], }, { name: "rev_tag", // Revocation tag memory diff --git a/rtl/bus/tl_ifetch_pkg.sv b/rtl/bus/tl_ifetch_pkg.sv index 641070ec9..647869f50 100644 --- a/rtl/bus/tl_ifetch_pkg.sv +++ b/rtl/bus/tl_ifetch_pkg.sv @@ -11,7 +11,7 @@ package tl_ifetch_pkg; localparam logic [31:0] ADDR_SPACE_DBG_DEV = 32'h b0000000; localparam logic [31:0] ADDR_MASK_SRAM = 32'h 0003ffff; - localparam logic [31:0] ADDR_MASK_HYPERRAM = 32'h 000fffff; + localparam logic [31:0] ADDR_MASK_HYPERRAM = 32'h 007fffff; localparam logic [31:0] ADDR_MASK_DBG_DEV = 32'h 00000fff; localparam int N_HOST = 1; diff --git a/rtl/bus/tl_main_pkg.sv b/rtl/bus/tl_main_pkg.sv index 2371e71eb..1606af8a6 100644 --- a/rtl/bus/tl_main_pkg.sv +++ b/rtl/bus/tl_main_pkg.sv @@ -32,7 +32,7 @@ package tl_main_pkg; localparam logic [31:0] ADDR_SPACE_RV_PLIC = 32'h 88000000; localparam logic [31:0] ADDR_MASK_SRAM = 32'h 0001ffff; - localparam logic [31:0] ADDR_MASK_HYPERRAM = 32'h 000fffff; + localparam logic [31:0] ADDR_MASK_HYPERRAM = 32'h 007fffff; localparam logic [31:0] ADDR_MASK_REV_TAG = 32'h 000007ff; localparam logic [31:0] ADDR_MASK_GPIO = 32'h 00000fff; localparam logic [31:0] ADDR_MASK_PINMUX = 32'h 00000fff; diff --git a/rtl/ip/hyperram/rtl/hbmc_tl_port.sv b/rtl/ip/hyperram/rtl/hbmc_tl_port.sv index 0a8759c25..4917ead3b 100644 --- a/rtl/ip/hyperram/rtl/hbmc_tl_port.sv +++ b/rtl/ip/hyperram/rtl/hbmc_tl_port.sv @@ -7,15 +7,23 @@ // in the event that writes are supported. // // An instruction port need not support write operations and does not require tag bits. +// +// Tag bits may be supported for only part of the mapped HyperRAM address range, in which case +// case an attempt to set a tag bit at an address that is outside that range will result in a +// TL-UL error being returned and the write will not occur. + module hbmc_tl_port import tlul_pkg::*; #( + // Width of HyperRAM address, in bits. parameter int unsigned HyperRAMAddrW = 20, + // Address width of the portion that can store capabilities, in bits. + parameter int unsigned HyperRAMTagAddrW = 19, // log2(burst length in bytes) parameter int unsigned Log2BurstLen = 5, // 32-byte bursts. parameter int unsigned NumBufs = 4, parameter int unsigned PortIDWidth = 1, parameter int unsigned Log2MaxBufs = 2, parameter int unsigned SeqWidth = 6, - // + // Does this port need to support TileLink write operations? parameter bit SupportWrites = 1, // Coalesce write transfers into burst writes to the HBMC? @@ -25,57 +33,57 @@ module hbmc_tl_port import tlul_pkg::*; #( localparam int unsigned ABIT = $clog2(top_pkg::TL_DW / 8), localparam int unsigned BBIT = Log2BurstLen ) ( - input clk_i, - input rst_ni, + input clk_i, + input rst_ni, // Constant indicating port number. - input [PortIDWidth-1:0] portid_i, + input [PortIDWidth-1:0] portid_i, // TL-UL interface. - input tl_h2d_t tl_i, - output tl_d2h_t tl_o, + input tl_h2d_t tl_i, + output tl_d2h_t tl_o, // Write notification input. - input wr_notify_i, - input [HyperRAMAddrW-1:ABIT] wr_notify_addr_i, - input [top_pkg::TL_DBW-1:0] wr_notify_mask_i, - input [top_pkg::TL_DW-1:0] wr_notify_data_i, + input wr_notify_i, + input [HyperRAMAddrW-1:ABIT] wr_notify_addr_i, + input [top_pkg::TL_DBW-1:0] wr_notify_mask_i, + input [top_pkg::TL_DW-1:0] wr_notify_data_i, // Write notification output. - output logic wr_notify_o, - output logic [top_pkg::TL_DBW-1:0] wr_notify_mask_o, - output logic [top_pkg::TL_DW-1:0] wr_notify_data_o, - output logic [HyperRAMAddrW-1:ABIT] wr_notify_addr_o, + output logic wr_notify_o, + output logic [top_pkg::TL_DBW-1:0] wr_notify_mask_o, + output logic [top_pkg::TL_DW-1:0] wr_notify_data_o, + output logic [HyperRAMAddrW-1:ABIT] wr_notify_addr_o, // Command data to the HyperRAM controller; command, address and burst length - output logic cmd_req_o, - input cmd_wready_i, - output logic [HyperRAMAddrW-1:ABIT] cmd_mem_addr_o, - output logic [Log2BurstLen-ABIT:0] cmd_word_cnt_o, - output logic cmd_wr_not_rd_o, - output logic cmd_wrap_not_incr_o, - output logic [SeqWidth-1:0] cmd_seq_o, - - output logic tag_cmd_req, - output logic [HyperRAMAddrW-1:ABIT] tag_cmd_mem_addr, - output logic tag_cmd_wr_not_rd, - output tag_cmd_wcap, - - output logic dfifo_wr_ena_o, - input dfifo_wr_full_i, - output [top_pkg::TL_DBW-1:0] dfifo_wr_strb_o, - output [top_pkg::TL_DW-1:0] dfifo_wr_din_o, + output logic cmd_req_o, + input cmd_wready_i, + output logic [HyperRAMAddrW-1:ABIT] cmd_mem_addr_o, + output logic [Log2BurstLen-ABIT:0] cmd_word_cnt_o, + output logic cmd_wr_not_rd_o, + output logic cmd_wrap_not_incr_o, + output logic [SeqWidth-1:0] cmd_seq_o, + + output logic tag_cmd_req, + output logic [HyperRAMTagAddrW-1:ABIT] tag_cmd_mem_addr, + output logic tag_cmd_wr_not_rd, + output tag_cmd_wcap, + + output logic dfifo_wr_ena_o, + input dfifo_wr_full_i, + output [top_pkg::TL_DBW-1:0] dfifo_wr_strb_o, + output [top_pkg::TL_DW-1:0] dfifo_wr_din_o, // Read data from the HyperRAM - output ufifo_rd_ena, - input ufifo_rd_empty, - input [top_pkg::TL_DW-1:0] ufifo_rd_dout, - input [SeqWidth-1:0] ufifo_rd_seq, - input ufifo_rd_last, + output ufifo_rd_ena, + input ufifo_rd_empty, + input [top_pkg::TL_DW-1:0] ufifo_rd_dout, + input [SeqWidth-1:0] ufifo_rd_seq, + input ufifo_rd_last, // Tag read data interface. - output tag_rdata_rready, - input tl_tag_bit + output tag_rdata_rready, + input tl_tag_bit ); /*----------------------------------------------------------------------------------------------------------------------------*/ @@ -84,10 +92,15 @@ module hbmc_tl_port import tlul_pkg::*; #( logic tl_req_fifo_le1; logic wr_notify_match; logic dfifo_wr_full; + logic rdbuf_matches; // Address matches within the read buffer. + logic rdbuf_valid; // Valid data is available within the read buffer. logic cmd_wready; logic can_accept; logic rdbuf_hit; logic rdbuf_re; + logic wr_err; + logic wr_req; + logic rd_req; logic issue; // We can accept an incoming TileLink transaction when we've got space in the hyperram, tag @@ -106,10 +119,22 @@ module hbmc_tl_port import tlul_pkg::*; #( (tl_i.a_opcode == Get || ~dfifo_wr_full) & ~(wr_notify_i & wr_notify_match); + // Return an error response for any capability write to an address that cannot support tags. + wire untagged_addr = |(tl_i.a_address[HyperRAMAddrW:0] >> HyperRAMTagAddrW) & + tl_i.a_user.capability; + /*----------------------------------------------------------------------------------------------------------------------------*/ - wire rd_req = tl_i.a_valid & (tl_i.a_opcode == Get); - wire wr_req = tl_i.a_valid & (tl_i.a_opcode == PutFullData || tl_i.a_opcode == PutPartialData); + // Valid read request? + assign rd_req = tl_i.a_valid & (tl_i.a_opcode == Get); + // Valid write request? + assign wr_req = tl_i.a_valid & (tl_i.a_opcode == PutFullData || tl_i.a_opcode == PutPartialData) & + (SupportWrites & !untagged_addr); + +/*----------------------------------------------------------------------------------------------------------------------------*/ + // Invalid write request? + assign wr_err = tl_i.a_valid & (tl_i.a_opcode == PutFullData || tl_i.a_opcode == PutPartialData) & + (untagged_addr | !SupportWrites); /*----------------------------------------------------------------------------------------------------------------------------*/ if (SupportWrites) begin @@ -138,10 +163,8 @@ module hbmc_tl_port import tlul_pkg::*; #( /*----------------------------------------------------------------------------------------------------------------------------*/ - logic rdbuf_matches; // Address matches within the read buffer. - logic rdbuf_valid; // Valid data is available within the read buffer. logic [SeqWidth-1:0] rdbuf_seq; // Sequence number of read buffer contents. - logic [top_pkg::TL_DW-1:0] rdbuf_dout; + logic [top_pkg::TL_DW-1:0] rdbuf_dout; // Invalidate the read buffer contents when a write occurs. // @@ -219,12 +242,21 @@ module hbmc_tl_port import tlul_pkg::*; #( localparam int unsigned TL_REQ_FIFO_DEPTH = 4; localparam int unsigned TLReqFifoDepthW = prim_util_pkg::vbits(TL_REQ_FIFO_DEPTH+1); - // Metadata from inbound TileLink transactions that needs to be saved to produce the response + // Verdict on the TL-UL request. + typedef enum logic [1:0] { + TLRspRdBuf, // Return buffered read data. + TLRspRdFetch, // Fetching read data from HBMC. + TLRspWrOk, // Valid write. + TLRspWrErr // Return error on write. + } tl_rsp_type_e; + + // Description of a queued TL-UL request-response. typedef struct packed { + // Metadata from inbound TileLink transactions that needs to be saved to produce the response. logic [top_pkg::TL_AIW-1:0] tl_source; logic [top_pkg::TL_SZW-1:0] tl_size; - logic cmd_fetch; - logic cmd_wr_not_rd; + // Response to be returned. + tl_rsp_type_e rsp_type; } tl_req_info_t; tl_req_info_t tl_req_fifo_wdata, tl_req_fifo_rdata; @@ -242,7 +274,7 @@ module hbmc_tl_port import tlul_pkg::*; #( // To be a contender in the arbitration among all ports, we need to express our intention // to write into the command buffer. wire cmd_req = tl_i.a_valid && tl_req_fifo_wready && !rdbuf_hit && - (tl_i.a_opcode == Get || ~dfifo_wr_full); + (tl_i.a_opcode == Get || (!dfifo_wr_full & !wr_err)); assign issue = tl_i.a_valid & can_accept; @@ -257,26 +289,24 @@ module hbmc_tl_port import tlul_pkg::*; #( if (issue) begin // Write to the relevant FIFOs and indicate ready on TileLink A channel - tag_cmd_req = 1'b1; + // - no tag request if the write was rejected. + tag_cmd_req = !wr_err; tl_req_fifo_wvalid = 1'b1; - - if (tl_i.a_opcode != Get) begin - dfifo_wr_ena = 1'b1; - end + // Write into the downstream FIFO only for a valid write. + dfifo_wr_ena = wr_req; end end assign tag_cmd_wr_not_rd = cmd_wr_not_rd; - assign tag_cmd_mem_addr = tl_i.a_address[HyperRAMAddrW-1:ABIT]; - - wire tl_cmd_fetch = ~(rd_req & rdbuf_valid); - wire tl_cmd_wr_not_rd = (tl_i.a_opcode != Get); + assign tag_cmd_mem_addr = tl_i.a_address[HyperRAMTagAddrW-1:ABIT]; + // Decide on the type of response to be sent; encoded using an enumeration to reduce FIFO width. + tl_rsp_type_e tl_cmd_rsp = (rd_req ? (rdbuf_valid ? TLRspRdBuf : TLRspRdFetch) + : (wr_err ? TLRspWrErr : TLRspWrOk)); assign tl_req_fifo_wdata = '{ tl_source : tl_i.a_source, tl_size : tl_i.a_size, - cmd_fetch : tl_cmd_fetch, - cmd_wr_not_rd : tl_cmd_wr_not_rd + rsp_type : tl_cmd_rsp }; // We decant the read data from the 'Upstream FIFO' into the read buffer as soon as possible, @@ -300,6 +330,11 @@ module hbmc_tl_port import tlul_pkg::*; #( logic [top_pkg::TL_DW-1:0] ufifo_dout_first; assign ufifo_dout_first = ufifo_rd_dout[top_pkg::TL_DW-1:0]; + // Decode control signals from the response type. + wire tl_rsp_wr_not_rd = (tl_req_fifo_rdata.rsp_type == TLRspWrOk || + tl_req_fifo_rdata.rsp_type == TLRspWrErr); + wire tl_rsp_rd_buffered = (tl_req_fifo_rdata.rsp_type == TLRspRdBuf); + // If the data from the read buffer is not accepted immediately by the host we must register it // to prevent it being invalidated by another read. logic rdata_valid_q; @@ -311,9 +346,9 @@ module hbmc_tl_port import tlul_pkg::*; #( if (tl_i.d_ready) rdata_valid_q <= 1'b0; // Response sent. else begin // Capture read data and keep it stable until it is accepted by the host. - rdata_valid_q <= !tl_req_fifo_rdata.cmd_wr_not_rd; + rdata_valid_q <= !tl_rsp_wr_not_rd; if (!rdata_valid_q) begin - rdata_q <= tl_req_fifo_rdata.cmd_fetch ? ufifo_dout_first : rdbuf_dout; + rdata_q <= tl_rsp_rd_buffered ? rdbuf_dout : ufifo_dout_first; end end end @@ -328,23 +363,23 @@ module hbmc_tl_port import tlul_pkg::*; #( tl_o_int = '0; if (tl_req_fifo_rvalid) begin // We have an incoming request that needs a response - if (tl_req_fifo_rdata.cmd_wr_not_rd) begin + if (tl_rsp_wr_not_rd) begin // If it's a write then return an immediate response (early response is reasonable as any // read that could observe the memory cannot occur until the write has actually happened) tl_o_int.d_valid = 1'b1; end else begin // Otherwise wait until we have the first word of data to return. tl_o_int.d_valid = |{ufifo_rd_ena & ~ufifo_rd_bursting, // Initial word of burst read. - ~tl_req_fifo_rdata.cmd_fetch, // From read buffer. + tl_rsp_rd_buffered, // From read buffer. rdata_valid_q}; // Holding read data stable until accepted. end end - - tl_o_int.d_opcode = tl_req_fifo_rdata.cmd_wr_not_rd ? AccessAck : AccessAckData; + tl_o_int.d_error = (tl_req_fifo_rdata.rsp_type == TLRspWrErr); + tl_o_int.d_opcode = tl_rsp_wr_not_rd ? AccessAck : AccessAckData; tl_o_int.d_size = tl_req_fifo_rdata.tl_size; tl_o_int.d_source = tl_req_fifo_rdata.tl_source; tl_o_int.d_data = rdata_valid_q ? rdata_q : - (tl_req_fifo_rdata.cmd_fetch ? ufifo_dout_first : rdbuf_dout); + (tl_rsp_rd_buffered ? rdbuf_dout : ufifo_dout_first); tl_o_int.d_user.capability = tl_tag_bit; tl_o_int.a_ready = issue; end @@ -354,7 +389,7 @@ module hbmc_tl_port import tlul_pkg::*; #( assign tl_req_fifo_rready = tl_o_int.d_valid & tl_i.d_ready; // Discard the tag read data once the _read_ data is accepted. - assign tag_rdata_rready = tl_o_int.d_valid & tl_i.d_ready & ~tl_req_fifo_rdata.cmd_wr_not_rd; + assign tag_rdata_rready = tl_o_int.d_valid & tl_i.d_ready & ~tl_rsp_wr_not_rd; // Generate integrity for outgoing response. tlul_rsp_intg_gen #( diff --git a/rtl/ip/hyperram/rtl/hbmc_tl_top.sv b/rtl/ip/hyperram/rtl/hbmc_tl_top.sv index c238fba9c..474dd9e35 100644 --- a/rtl/ip/hyperram/rtl/hbmc_tl_top.sv +++ b/rtl/ip/hyperram/rtl/hbmc_tl_top.sv @@ -60,7 +60,10 @@ module hbmc_tl_top import tlul_pkg::*; #( parameter [4:0] C_DQ0_IDELAY_TAPS_VALUE = 0, parameter int unsigned NumPorts = 2, - parameter integer HyperRAMSize = 1024 * 1024 // 1 MiB + // Mapped size of the HyperRAM, in bytes. + parameter int unsigned HyperRAMSize = 8 * 1024 * 1024, // 8 MiB + // Mapped portion of the HyperRAM that can store capabilities, in bytes. + parameter int unsigned HyperRAMTagSize = 4 * 1024 * 1024 // 4 MiB ) ( input clk_i, @@ -97,8 +100,10 @@ module hbmc_tl_top import tlul_pkg::*; #( // bits to identify the buffer number, and a further bit for the port number of // the requester. localparam int unsigned SeqWidth = PortIDWidth + Log2MaxBufs + 3; - + // Width of HyperRAM address, in bits. localparam int unsigned HyperRAMAddrW = $clog2(HyperRAMSize); + // Address width of the portion that can store capabilities, in bits. + localparam int unsigned HyperRAMTagAddrW = $clog2(HyperRAMTagSize); // LSB of word address. localparam int unsigned ABIT = $clog2(top_pkg::TL_DW / 8); // Use 32-byte bursts for performance, whilst reducing the penalty of wasted burst reads. @@ -256,7 +261,7 @@ module hbmc_tl_top import tlul_pkg::*; #( logic [NumPorts-1:0] dfifo_all_wr_full; logic [NumPorts-1:0] tag_all_cmd_req; - logic [NumPorts-1:0][HyperRAMAddrW-1:ABIT] tag_all_cmd_mem_addr; + logic [NumPorts-1:0][HyperRAMTagAddrW-1:ABIT] tag_all_cmd_mem_addr; logic [NumPorts-1:0] tag_all_cmd_wr_not_rd; logic [NumPorts-1:0] tag_all_rdata_rready; @@ -290,13 +295,14 @@ module hbmc_tl_top import tlul_pkg::*; #( for (genvar p = 0; p < NumPorts; p++) begin : gen_ports hbmc_tl_port #( - .HyperRAMAddrW (HyperRAMAddrW), - .Log2BurstLen (Log2BurstLen), - .NumBufs (MaxBufs), - .PortIDWidth (PortIDWidth), - .Log2MaxBufs (Log2MaxBufs), - .SeqWidth (SeqWidth), - .SupportWrites (p == PortD) // Only the data port supports writing. + .HyperRAMAddrW (HyperRAMAddrW), + .HyperRAMTagAddrW (HyperRAMTagAddrW), + .Log2BurstLen (Log2BurstLen), + .NumBufs (MaxBufs), + .PortIDWidth (PortIDWidth), + .Log2MaxBufs (Log2MaxBufs), + .SeqWidth (SeqWidth), + .SupportWrites (p == PortD) // Only the data port supports writing. ) u_port( .clk_i (clk_i), .rst_ni (rst_ni), @@ -574,8 +580,8 @@ module hbmc_tl_top import tlul_pkg::*; #( // Command FIFO provides reads and writes from tilelink requests. Writes just happen without any // response and reads writes to rdata_fifo to be picked up by the tilelink response. - // 1 tag bit per 64 bits so divide HyperRAMSize by 8 - localparam TAG_ADDR_W = $clog2(HyperRAMSize) - 3; + // 1 tag bit per 64 bits so divide HyperRAMTagSize by 8 + localparam TAG_ADDR_W = HyperRAMTagAddrW - 3; localparam TAG_FIFO_DEPTH = 4; typedef struct packed { @@ -590,7 +596,7 @@ module hbmc_tl_top import tlul_pkg::*; #( // Only the Data port requires capability tags. tag_cmd_t tag_cmd; - assign tag_cmd.addr = tag_all_cmd_mem_addr[PortD][HyperRAMAddrW-1:ABIT+1]; + assign tag_cmd.addr = tag_all_cmd_mem_addr[PortD][HyperRAMTagAddrW-1:ABIT+1]; assign tag_cmd.write = tag_all_cmd_wr_not_rd[PortD]; assign tag_cmd.wdata = tag_all_cmd_wcap[PortD]; diff --git a/rtl/ip/hyperram/rtl/hyperram.sv b/rtl/ip/hyperram/rtl/hyperram.sv index bccbc5620..876fee8b0 100644 --- a/rtl/ip/hyperram/rtl/hyperram.sv +++ b/rtl/ip/hyperram/rtl/hyperram.sv @@ -9,8 +9,11 @@ // particular require Xilinx encrypted IP models). module hyperram import tlul_pkg::*; #( - parameter HyperRAMClkFreq = 200_000_000, - parameter HyperRAMSize = 1024 * 1024, + parameter int unsigned HyperRAMClkFreq = 200_000_000, + // Mapped size of the HyperRAM, in bytes. + parameter int unsigned HyperRAMSize = 8 * 1024 * 1024, // 8 MiB + // Mapped portion of the HyperRAM that can store capabilities, in bytes. + parameter int unsigned HyperRAMTagSize = 4 * 1024 * 1024, // 4 MiB // Number of access ports. parameter int unsigned NumPorts = 2 ) ( @@ -37,7 +40,7 @@ module hyperram import tlul_pkg::*; #( // behaviour/timing of the HyperRAM is not required. It is also required in synthesis for // the Sonata XL board because that has no HyperRAM. localparam int SRAMModelAddrWidth = $clog2(HyperRAMSize); - localparam int UnusedParams = HyperRAMClkFreq + HyperRAMSize; + localparam int UnusedParams = HyperRAMClkFreq + HyperRAMSize + HyperRAMTagSize; logic unused_clk_hr; logic unused_clk_hr90p; @@ -120,7 +123,8 @@ module hyperram import tlul_pkg::*; #( .C_DQ0_IDELAY_TAPS_VALUE(0), .C_ISERDES_CLOCKING_MODE(0), .NumPorts(NumPorts), - .HyperRAMSize(HyperRAMSize) + .HyperRAMSize(HyperRAMSize), + .HyperRAMTagSize(HyperRAMTagSize) ) u_hbmc_tl_top ( .clk_i(clk_i), .rst_ni(rst_ni), diff --git a/rtl/system/sonata_system.sv b/rtl/system/sonata_system.sv index 793760203..5273a718b 100644 --- a/rtl/system/sonata_system.sv +++ b/rtl/system/sonata_system.sv @@ -116,20 +116,21 @@ module sonata_system // Signals, types and parameters for system. // /////////////////////////////////////////////// - localparam int unsigned MemSize = 128 * 1024; // 128 KiB - localparam int unsigned SRAMAddrWidth = $clog2(MemSize); - localparam int unsigned HyperRAMSize = 1024 * 1024; // 1 MiB - localparam int unsigned PwmCtrSize = 8; - localparam int unsigned BusAddrWidth = 32; - localparam int unsigned BusByteEnable = 4; - localparam int unsigned BusDataWidth = 32; - localparam int unsigned DRegAddrWidth = 12; // Debug module uses 12 bits of addressing. - localparam int unsigned TRegAddrWidth = 16; // Timer uses more address bits. - localparam int unsigned FixedSpiNum = 2; // Number of SPI devices that don't pass through the pinmux - localparam int unsigned TotalSpiNum = SPI_NUM + FixedSpiNum; // The total number of SPI devices - localparam int unsigned FixedGpioNum = 1; // Number of GPIO instances that don't pass through the pinmux - localparam int unsigned TotalGpioNum = GPIO_NUM + FixedGpioNum; // The total number of GPIO instances - localparam int unsigned TAccessLatency = 0; // Cycles of read data latency. + localparam int unsigned MemSize = 128 * 1024; // 128 KiB + localparam int unsigned SRAMAddrWidth = $clog2(MemSize); + localparam int unsigned HyperRAMSize = 8 * 1024 * 1024; // 8 MiB + localparam int unsigned HyperRAMTagSize = 4 * 1024 * 1024; // 4 MiB which can hold capabilities. + localparam int unsigned PwmCtrSize = 8; + localparam int unsigned BusAddrWidth = 32; + localparam int unsigned BusByteEnable = 4; + localparam int unsigned BusDataWidth = 32; + localparam int unsigned DRegAddrWidth = 12; // Debug module uses 12 bits of addressing. + localparam int unsigned TRegAddrWidth = 16; // Timer uses more address bits. + localparam int unsigned FixedSpiNum = 2; // Number of SPI devices that don't pass through the pinmux + localparam int unsigned TotalSpiNum = SPI_NUM + FixedSpiNum; // The total number of SPI devices + localparam int unsigned FixedGpioNum = 1; // Number of GPIO instances that don't pass through the pinmux + localparam int unsigned TotalGpioNum = GPIO_NUM + FixedGpioNum; // The total number of GPIO instances + localparam int unsigned TAccessLatency = 0; // Cycles of read data latency. // The number of data bits controlled by each mask bit; since the CPU requires // only byte level access, explicitly grouping the data bits makes the inferred @@ -521,6 +522,7 @@ module sonata_system hyperram #( .HyperRAMClkFreq ( HyperRAMClkFreq ), .HyperRAMSize ( HyperRAMSize ), + .HyperRAMTagSize ( HyperRAMTagSize ), .NumPorts ( 2 ) ) u_hyperram ( .clk_i (clk_sys_i), From f5cbd97b0419adf118c2c2ceec48cc94b0ce4fd6 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Wed, 3 Sep 2025 22:19:56 +0100 Subject: [PATCH 2/4] Tidy ups to the HyperRAM-related assembler Tidy and explain `get_hyperram_fn_ptr.` Remove misleading and incorrect comment; this is no return value. --- sw/cheri/common/hyperram_exec_test.S | 17 ++++++++++++----- sw/cheri/common/hyperram_perf_test.S | 1 - 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/sw/cheri/common/hyperram_exec_test.S b/sw/cheri/common/hyperram_exec_test.S index 2b9d3e11c..e6d82ef56 100644 --- a/sw/cheri/common/hyperram_exec_test.S +++ b/sw/cheri/common/hyperram_exec_test.S @@ -1,14 +1,21 @@ # Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -.include "assembly-helpers.s" + .include "assembly-helpers.s" .section .text, "ax", @progbits .globl get_hyperram_fn_ptr .p2align 2 - .type get_hyperram_fn_ptr,@function + .type get_hyperram_fn_ptr,@function + +// Return an executable capability for the given address; this relies upon pcc containing the +// supplied address already. +// +// entry a0 = address of function +// return ca0 -> function get_hyperram_fn_ptr: - auipcc ct0, 0 - csetaddr ca0, ct0, a0 - cret + auipcc ct0, 0 + csetaddr ca0, ct0, a0 + cret + diff --git a/sw/cheri/common/hyperram_perf_test.S b/sw/cheri/common/hyperram_perf_test.S index 8f125d445..2cbce4855 100644 --- a/sw/cheri/common/hyperram_perf_test.S +++ b/sw/cheri/common/hyperram_perf_test.S @@ -31,7 +31,6 @@ // ca0 -> destination (word-aligned) // ca1 -> source (word-aligned) // a2 = number of bytes to copy -// return -> beyond destination data. hyperram_copy_block: srl a3, a2, 5 andi a2, a2, 31 // 0-31 bytes remaining after 32-byte loop. From 8593772be4bda196f7de1f378fa1f7f63747f905 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Tue, 2 Sep 2025 20:02:51 +0100 Subject: [PATCH 3/4] Update HyperRAM test software to reflect size changes Introduce a test for whether storing a capability to an untagged area of memory raises an exception as intended. Stores to areas of the HyperRAM that have associated tag bit storage should complete without an exception. In each case, check whether an exception does/does not occur as expected, and check the contents of the memory after the attempted store. An exception handler resumes execution after the faulting instruction but - importantly - is activated only for the very short time window during which we expect the possible exception to occur. --- sw/cheri/checks/hyperram_memset.S | 6 + sw/cheri/checks/hyperram_memset.h | 3 + sw/cheri/checks/hyperram_test.cc | 183 ++++++++++++++++++++++++++++-- sw/cheri/common/sonata-devices.hh | 5 +- sw/cheri/tests/hyperram_tests.hh | 23 ++-- sw/common/defs.h | 4 +- 6 files changed, 207 insertions(+), 17 deletions(-) diff --git a/sw/cheri/checks/hyperram_memset.S b/sw/cheri/checks/hyperram_memset.S index f64b8a1f8..a2d22de0f 100644 --- a/sw/cheri/checks/hyperram_memset.S +++ b/sw/cheri/checks/hyperram_memset.S @@ -249,3 +249,9 @@ memset_cd_8fix: bgtz a2, memset_b_desc_tail cret +// Utility function returning the `pcc` at which an exception occurred. + .globl get_mepcc + .p2align 5 +get_mepcc: + cspecialr ca0, mepcc + cret diff --git a/sw/cheri/checks/hyperram_memset.h b/sw/cheri/checks/hyperram_memset.h index 5063620be..51423b566 100644 --- a/sw/cheri/checks/hyperram_memset.h +++ b/sw/cheri/checks/hyperram_memset.h @@ -30,3 +30,6 @@ extern "C" void hyperram_memset_wr(volatile uint32_t *dst, int c, size_t n); extern "C" void hyperram_memset_wd(volatile uint32_t *dst, int c, size_t n); extern "C" void hyperram_memset_c(volatile uint64_t *dst, int c, size_t n); extern "C" void hyperram_memset_cd(volatile uint64_t *dst, int c, size_t n); + +// Utility function to return the address at which an exception occurred. +extern "C" void *get_mepcc(void); diff --git a/sw/cheri/checks/hyperram_test.cc b/sw/cheri/checks/hyperram_test.cc index e325a9f03..33dc2a8d3 100644 --- a/sw/cheri/checks/hyperram_test.cc +++ b/sw/cheri/checks/hyperram_test.cc @@ -23,10 +23,59 @@ #include "hyperram_memset.h" +// Simulation is much slower than execution on FPGA and these tests are primarily intended for +// FPGA-based testing. Define this to 1 for use in simulation. +#define SIMULATION 1 + using namespace CHERI; const int RandTestBlockSize = 256; -const int HyperramSize = (1024 * 1024) / 4; +#if SIMULATION +// Note that this means many of the tests will be exercising only small fraction of the mapped +// HyperRAM address range. +const unsigned HyperramSize = HYPERRAM_BOUNDS / 1024; +#else +// Number of 32-bit words within the mapped HyperRAM. +const unsigned HyperramSize = HYPERRAM_BOUNDS / 4; +#endif + +// The amount of HyperRAM that supports capability stores. +const unsigned HyperramTagSize = HYPERRAM_TAG_BOUNDS / 4; + +// Logging from the exception handling code. +static Log *exc_log = NULL; + +// Signals whether an exception should be trapped and the faulting instruction skipped. +static volatile bool trap_err = false; + +// Records whether an attempt to store a capability to the HyperRAM resulted in a +// TL-UL bus error and thus an exception. +static volatile bool act_err = false; + +// TODO: #429 Presently the debugger cannot perform sub-word writes, so pad the BSS to 4 bytes. +volatile uint16_t dummy; + +extern "C" void exception_handler(void) { + if (trap_err) { + // Record the fact that an exception occurred. + act_err = true; + // Advance over the failing instruction; this is a `csc` instruction but it may or may not be + // compressed. + __asm volatile( + " cspecialr ct0, mepcc\n" + " lh t2, 0(ct0)\n" + " li t1, 3\n" + " and t2, t2, t1\n" + " bne t2, t1, instr16\n" + " cincoffset ct0, ct0, 2\n" + "instr16: cincoffset ct0, ct0, 2\n" + "update: cspecialw mepcc, ct0"); + } else if (exc_log) { + uint32_t exc_addr = __builtin_cheri_address_get(get_mepcc()); + exc_log->println("Unexpected exception occurred at {:#x}", exc_addr); + while (1) asm volatile(" "); + } +} // Ensure that all writing of code to memory has completed before commencing execution // of that code. Code has been written to [start, end] with both addresses being @@ -129,10 +178,10 @@ int rand_cap_test(Capability hyperram_area, Capability read_cap; do { - rand_index = prng() % HyperramSize; + rand_index = prng() % HyperramTagSize; // Capability is double word in size. - rand_cap_index = prng() % (HyperramSize / 2); + rand_cap_index = prng() % (HyperramTagSize / 2); } while (rand_index / 2 == rand_cap_index); rand_val = prng(); @@ -670,6 +719,110 @@ int linear_execution_test(Capability hyperram_w_area, ds::xor return failures; } +// Simple test of whether the full HyperRAM is mapped, as well checking that capabilities can +// only be stored to the intended portion of this mapped range. +int mapped_tagged_range_test(Capability hyperram_w_area, + Capability> hyperram_cap_area, ds::xoroshiro::P64R32 &prng, + Log &log, int iterations = 1) { + const bool verbose = false; + int failures = 0; + + // In the event that the entire HyperRAM supports capabilities, we must reduce two of our + // directed choices to be within bounds. + uint32_t tag_bounds_plus_8 = HYPERRAM_TAG_BOUNDS + 8; + uint32_t tag_bounds = HYPERRAM_TAG_BOUNDS; + if (tag_bounds_plus_8 >= HYPERRAM_BOUNDS) { + tag_bounds_plus_8 = HYPERRAM_BOUNDS - 8; + tag_bounds = HYPERRAM_BOUNDS - 16; + } + + for (int iter = 0; iter < iterations; ++iter) { + Capability read_cap; + unsigned rand_choice = prng() & 7u; + uint32_t rand_addr; + + switch (rand_choice) { + // Directed choices. + case 0u: + rand_addr = tag_bounds; + break; + case 1u: + rand_addr = HYPERRAM_TAG_BOUNDS - 8; + break; + case 2u: + rand_addr = HYPERRAM_BOUNDS - 8; + break; + case 3u: + rand_addr = tag_bounds_plus_8; + break; + case 4u: + rand_addr = 0u; + break; + // Randomised choices. + default: + rand_addr = prng() & (HYPERRAM_BOUNDS - 8u); + break; + } + + // Predict whether we should see a TL-UL error in response; only the first portion of the + // mapped HyperRAM supports tag bits. Anything at a higher address should raise a TL-UL error. + bool exp_err = (rand_addr >= HYPERRAM_TAG_BOUNDS); + if (verbose) { + log.println("addr {:#x}", rand_addr); + } + + // We are expecting to generate a TL-UL error with some store operations. + trap_err = true; + // First store some data that does not constitute a sensible capability. + const uint32_t exp_data1 = 0x87654321u; + const uint32_t exp_data0 = ~0u; + hyperram_w_area[rand_addr >> 2] = exp_data0; + hyperram_w_area[(rand_addr >> 2) + 1] = exp_data1; + + // Attempt to store a capability to the chosen address. + // The capability stored doesn't really matter; just use the HyperRAM base. + act_err = false; + hyperram_cap_area[rand_addr >> 3] = hyperram_w_area; + trap_err = false; + if (verbose) { + log.println("done write"); + } + read_cap = hyperram_cap_area[rand_addr >> 3]; + + // Check that an error occurred iff expected. + failures += (act_err != exp_err); + if (verbose) { + log.println("Act err {}, exp err {}", (int)act_err, (int)exp_err); + } + + // Check the memory contents. + if (exp_err) { + // If an error occurred then we expect _not_ to have performed the write, so the test data + // should still be intact. + uint32_t act_data1 = hyperram_w_area[(rand_addr >> 2) + 1]; + uint32_t act_data0 = hyperram_w_area[rand_addr >> 2]; + if (verbose) { + log.println("Wrote {:#x}:{:#x}, read back {:#x}:{:#x}", exp_data0, exp_data1, act_data0, act_data1); + } + if (exp_data0 != act_data0 || exp_data1 != act_data1) { + failures++; + } + } else { + // If there was no error, the capability should have been stored as expected. + if (verbose) { + volatile uint32_t *exp = hyperram_w_area.get(); + volatile uint32_t *act = read_cap.get(); + log.println("Wrote {:#x}, read back {:#x}", (uint32_t)exp, (uint32_t)act); + } + if (read_cap != hyperram_w_area) { + failures++; + } + } + } + + return failures; +} + /** * C++ entry point for the loader. This is called from assembly, with the * read-write root in the first argument. @@ -685,6 +838,8 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) { uart0->init(BAUD_RATE); WriteUart uart{uart0}; Log log(uart); + // Make logging available to the exception handler. + exc_log = &log; set_console_mode(log, CC_BOLD); log.print("\r\n\r\nGet hyped for hyperram!\r\n"); @@ -694,24 +849,32 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) { prng.set_state(0xDEADBEEF, 0xBAADCAFE); // Default is word-based accesses, which is sufficient for most tests. + // + // Unfortunately it is not possible to construct a capability that covers exactly the 8MiB range + // of the HyperRAM because of the encoding limitations of the CHERIoT capabilities. + // + // Here, in this manually-invoked test, we resort to mapping twice that address range because + // it is more important that we test all of the physical HyperRAM connectivity and logic, rather + // than the CHERIoT capabilities. Capability hyperram_area = root.cast(); + uint32_t bounds = 2 * HYPERRAM_BOUNDS; hyperram_area.address() = HYPERRAM_ADDRESS; - hyperram_area.bounds() = HYPERRAM_BOUNDS; + hyperram_area.bounds() = bounds; Capability> hyperram_cap_area = root.cast>(); hyperram_cap_area.address() = HYPERRAM_ADDRESS; - hyperram_cap_area.bounds() = HYPERRAM_BOUNDS; + hyperram_cap_area.bounds() = bounds; // We also want byte, hword and dword access for some tests. Capability hyperram_b_area = root.cast(); hyperram_b_area.address() = HYPERRAM_ADDRESS; - hyperram_b_area.bounds() = HYPERRAM_BOUNDS; + hyperram_b_area.bounds() = bounds; Capability hyperram_h_area = root.cast(); hyperram_h_area.address() = HYPERRAM_ADDRESS; - hyperram_h_area.bounds() = HYPERRAM_BOUNDS; + hyperram_h_area.bounds() = bounds; Capability hyperram_d_area = root.cast(); hyperram_d_area.address() = HYPERRAM_ADDRESS; - hyperram_d_area.bounds() = HYPERRAM_BOUNDS; + hyperram_d_area.bounds() = bounds; // Run indefinitely, soak testing until we observe one or more failures. int failures = 0; @@ -821,6 +984,10 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) { } log.print(" result..."); write_test_result(log, failures); + + log.println("Running mapped/tagged range test..."); + failures += mapped_tagged_range_test(hyperram_area, hyperram_cap_area, prng, log, 0x400u); + write_test_result(log, failures); } // Report test failure. diff --git a/sw/cheri/common/sonata-devices.hh b/sw/cheri/common/sonata-devices.hh index 828dd5490..f4845912a 100644 --- a/sw/cheri/common/sonata-devices.hh +++ b/sw/cheri/common/sonata-devices.hh @@ -77,9 +77,12 @@ using PinmuxPtrs = std::pair; } [[maybe_unused]] static HyperramPtr hyperram_ptr(CapRoot root) { + // Unfortunately it is not possible to construct a capability that covers exactly the 8MiB range + // of the HyperRAM because of the encoding limitations of the CHERIoT capabilities. + // We therefore leave the final 16KiB inaccessible. CHERI::Capability hyperram = root.cast(); hyperram.address() = HYPERRAM_ADDRESS; - hyperram.bounds() = HYPERRAM_BOUNDS; + hyperram.bounds() = HYPERRAM_BOUNDS - 0x4000u; return hyperram; } diff --git a/sw/cheri/tests/hyperram_tests.hh b/sw/cheri/tests/hyperram_tests.hh index 930a21461..be97f45f8 100644 --- a/sw/cheri/tests/hyperram_tests.hh +++ b/sw/cheri/tests/hyperram_tests.hh @@ -33,19 +33,24 @@ using namespace CHERI; * It can be overwride with a compilation flag */ #ifndef TEST_COVERAGE_AREA -// Test only 1% of the total memory to be fast enough for Verilator. -#define TEST_COVERAGE_AREA 1 +// Test only n/1024 of the total memory to be fast enough for Verilator. +// +// Note: we are deliberately using power-of-two quantities here because of the addressing +// limitations of CHERIoT capabilities. There is 8MiB available but we must keep the +// testing short, so we choose to use just ca. 0.2% +#define TEST_COVERAGE_AREA 2 #endif -_Static_assert(TEST_COVERAGE_AREA <= 100, "TEST_COVERAGE_AREA Should be less than 100"); +static_assert(TEST_COVERAGE_AREA <= 1024, "TEST_COVERAGE_AREA Should be less than 1024"); #define TEST_BLOCK_SIZE 256 -#define HYPERRAM_SIZE (1024 * 1024) / 4 +// Size of mapped, tag-capable portion of HyperRAM, in 32-bit words. +#define HYPERRAM_TAG_SIZE (HYPERRAM_TAG_BOUNDS / 4) /* * Compute the number of addresses that will be tested. * We mask the LSB 8bits to makes sure it is aligned. */ -#define HYPERRAM_TEST_SIZE (uint32_t)((HYPERRAM_SIZE * TEST_COVERAGE_AREA / 100) & ~0xFF) +#define HYPERRAM_TEST_SIZE (uint32_t)((HYPERRAM_TAG_SIZE * TEST_COVERAGE_AREA / 0x400u) & ~0xFF) /* * Write random values to a block of memory (size given by 'TEST_BLOCK_SIZE' @@ -301,11 +306,15 @@ int perf_burst_test(Capability hyperram_area, ds::xoroshiro:: } void hyperram_tests(CapRoot root, Log &log) { - auto hyperram_area = hyperram_ptr(root); + // Unfortunately it is not possible to construct a capability that covers exactly the 8MiB range + // of the HyperRAM because of the encoding limitations of the CHERIoT capabilities, but here we + // are only concerned with testing a much smaller portion of the address range anyway. + const uint32_t hr_bounds = HYPERRAM_TEST_SIZE * 4u; + auto hyperram_area = hyperram_ptr(root); Capability> hyperram_cap_area = root.cast>(); hyperram_cap_area.address() = HYPERRAM_ADDRESS; - hyperram_cap_area.bounds() = HYPERRAM_BOUNDS; + hyperram_cap_area.bounds() = hr_bounds; ds::xoroshiro::P64R32 prng; prng.set_state(0xDEADBEEF, 0xBAADCAFE); diff --git a/sw/common/defs.h b/sw/common/defs.h index 1e15372f9..94fee569a 100644 --- a/sw/common/defs.h +++ b/sw/common/defs.h @@ -13,7 +13,9 @@ #define SRAM_BOUNDS (0x0002'0000) #define HYPERRAM_ADDRESS (0x4000'0000) -#define HYPERRAM_BOUNDS (0x0010'0000) +#define HYPERRAM_BOUNDS (0x0080'0000) +// The portion of the HyperRAM that can support capabilities. +#define HYPERRAM_TAG_BOUNDS (0x0040'0000) #define SYSTEM_INFO_ADDRESS (0x8000'C000) #define SYSTEM_INFO_BOUNDS (0x0000'0020) From b9c99f1965cbf1a508a1ffe355b0cdc5556750c1 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Wed, 10 Sep 2025 13:57:33 +0100 Subject: [PATCH 4/4] Present memory sizes and DNA value in system info Present the following memory dimensions in the system info: - Main memory (SRAM). - HyperRAM. - Portion of the HyperRAM that is supported by tag bits an may thus store capabilities. Make the DNA value of the FPGA available in the system info IP block; this is a 57-bit value programmed into the FPGA which is described as 'most often unique', although there may be up to 32 devices in the FPGA family which have the same DNA value. Model the DNA_PORT/value in simulation, using a single value for all simulations. --- dv/models/fpga/rtl/DNA_PORT.v | 25 +++ rtl/ip/system_info/data/system_info.hjson | 69 +++++++ rtl/ip/system_info/rtl/system_info_reg_pkg.sv | 110 +++++++++--- rtl/ip/system_info/rtl/system_info_reg_top.sv | 168 ++++++++++++++++-- .../system_info/templates/system_info.sv.tpl | 35 +++- rtl/system/sonata_system.sv | 13 +- sonata.core | 1 + sw/cheri/checks/dma_check.cc | 114 ++++++++++++ sw/cheri/checks/system_info_check.cc | 58 ++++-- sw/common/defs.h | 2 +- 10 files changed, 525 insertions(+), 70 deletions(-) create mode 100644 dv/models/fpga/rtl/DNA_PORT.v create mode 100644 sw/cheri/checks/dma_check.cc diff --git a/dv/models/fpga/rtl/DNA_PORT.v b/dv/models/fpga/rtl/DNA_PORT.v new file mode 100644 index 000000000..5a938a6c3 --- /dev/null +++ b/dv/models/fpga/rtl/DNA_PORT.v @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module DNA_PORT( + input CLK, + input READ, + input SHIFT, + input DIN, + output DOUT +); + +reg [56:0] dna; +always @(posedge CLK) begin + if (READ) begin + // This arbitrary number uniquely identifies the simulation model. + dna <= 57'h1b8a94d_76732894; + end else if (SHIFT) begin + // User can extend the DNA value. + dna <= {dna[55:0], DIN}; + end +end +assign DOUT = dna[56]; + +endmodule diff --git a/rtl/ip/system_info/data/system_info.hjson b/rtl/ip/system_info/data/system_info.hjson index 66c095baa..2c663d648 100644 --- a/rtl/ip/system_info/data/system_info.hjson +++ b/rtl/ip/system_info/data/system_info.hjson @@ -130,5 +130,74 @@ }, ], }, + { name: "DNA_CAPTURE", + desc: '''Capture the `DNA value` of this Sonata device for reading via the `DNA` register. + A write to this register will present the first bit of the DNA value in `DNA.` + The value written does not matter.''', + swaccess: "wo", + hwaccess: "hro", + hwqe: "true", + hwext: "true", + fields: [ + { bits: "0", + name: "CAPTURE" + desc: '''Initiate reading of `DNA value`.''', + }, + ] + } + { name: "DNA", + desc: '''The next bit of the DNA value. + Reading of the DNA value should be initiated by writing to `DNA_CAPTURE.` + A single bit of the 57-bit DNA value is presented per read of the `DNA` register. + Up to 32 FPGA devices within a family may have the same DNA value.''', + swaccess: "ro", + hwaccess: "hrw", + hwre: "true", + hwext: "true", + fields: [ + { bits: "0", + name: "DNA" + desc: '''Next bit of DNA value.''', + }, + ] + } + { name: "MEM_SIZE", + desc: '''Size of main memory (SRAM) in KiB.''', + swaccess: "ro", + hwaccess: "hwo", + hwext: "true", + fields: [ + { bits: "31:0", + name: "SIZE", + desc: '''Size of main memory in KiB.''', + }, + ] + } + { name: "HYPERRAM_SIZE", + desc: '''Size of HyperRAM in KiB.''', + swaccess: "ro", + hwaccess: "hwo", + hwext: "true", + fields: [ + { bits: "31:0", + name: "SIZE", + desc: '''Size of HyperRAM in KiB.''', + }, + ] + } + { name: "HYPERRAM_TAG_SIZE", + desc: '''Size of tagged portion of HyperRAM in KiB. + It may be the case that only the first portion of the HyperRAM supports tags. + Storing a capability within the remainder of the HyperRAM will raise an error.''', + swaccess: "ro", + hwaccess: "hwo", + hwext: "true", + fields: [ + { bits: "31:0", + name: "SIZE", + desc: '''Size of tagged portion of HyperRAM in KiB.''', + }, + ] + } ], } diff --git a/rtl/ip/system_info/rtl/system_info_reg_pkg.sv b/rtl/ip/system_info/rtl/system_info_reg_pkg.sv index 293c7630e..f35f12049 100644 --- a/rtl/ip/system_info/rtl/system_info_reg_pkg.sv +++ b/rtl/ip/system_info/rtl/system_info_reg_pkg.sv @@ -7,12 +7,22 @@ package system_info_reg_pkg; // Address widths within the block - parameter int BlockAw = 5; + parameter int BlockAw = 6; //////////////////////////// // Typedefs for registers // //////////////////////////// + typedef struct packed { + logic q; + logic qe; + } system_info_reg2hw_dna_capture_reg_t; + + typedef struct packed { + logic q; + logic re; + } system_info_reg2hw_dna_reg_t; + typedef struct packed { logic [31:0] d; } system_info_hw2reg_rtl_commit_hash_0_reg_t; @@ -45,27 +55,58 @@ package system_info_reg_pkg; logic [7:0] d; } system_info_hw2reg_spi_info_reg_t; + typedef struct packed { + logic d; + } system_info_hw2reg_dna_reg_t; + + typedef struct packed { + logic [31:0] d; + } system_info_hw2reg_mem_size_reg_t; + + typedef struct packed { + logic [31:0] d; + } system_info_hw2reg_hyperram_size_reg_t; + + typedef struct packed { + logic [31:0] d; + } system_info_hw2reg_hyperram_tag_size_reg_t; + + // Register -> HW type + typedef struct packed { + system_info_reg2hw_dna_capture_reg_t dna_capture; // [3:2] + system_info_reg2hw_dna_reg_t dna; // [1:0] + } system_info_reg2hw_t; + // HW -> register type typedef struct packed { - system_info_hw2reg_rtl_commit_hash_0_reg_t rtl_commit_hash_0; // [128:97] - system_info_hw2reg_rtl_commit_hash_1_reg_t rtl_commit_hash_1; // [96:65] - system_info_hw2reg_rtl_commit_dirty_reg_t rtl_commit_dirty; // [64:64] - system_info_hw2reg_system_frequency_reg_t system_frequency; // [63:32] - system_info_hw2reg_gpio_info_reg_t gpio_info; // [31:24] - system_info_hw2reg_uart_info_reg_t uart_info; // [23:16] - system_info_hw2reg_i2c_info_reg_t i2c_info; // [15:8] - system_info_hw2reg_spi_info_reg_t spi_info; // [7:0] + system_info_hw2reg_rtl_commit_hash_0_reg_t rtl_commit_hash_0; // [225:194] + system_info_hw2reg_rtl_commit_hash_1_reg_t rtl_commit_hash_1; // [193:162] + system_info_hw2reg_rtl_commit_dirty_reg_t rtl_commit_dirty; // [161:161] + system_info_hw2reg_system_frequency_reg_t system_frequency; // [160:129] + system_info_hw2reg_gpio_info_reg_t gpio_info; // [128:121] + system_info_hw2reg_uart_info_reg_t uart_info; // [120:113] + system_info_hw2reg_i2c_info_reg_t i2c_info; // [112:105] + system_info_hw2reg_spi_info_reg_t spi_info; // [104:97] + system_info_hw2reg_dna_reg_t dna; // [96:96] + system_info_hw2reg_mem_size_reg_t mem_size; // [95:64] + system_info_hw2reg_hyperram_size_reg_t hyperram_size; // [63:32] + system_info_hw2reg_hyperram_tag_size_reg_t hyperram_tag_size; // [31:0] } system_info_hw2reg_t; // Register offsets - parameter logic [BlockAw-1:0] SYSTEM_INFO_RTL_COMMIT_HASH_0_OFFSET = 5'h 0; - parameter logic [BlockAw-1:0] SYSTEM_INFO_RTL_COMMIT_HASH_1_OFFSET = 5'h 4; - parameter logic [BlockAw-1:0] SYSTEM_INFO_RTL_COMMIT_DIRTY_OFFSET = 5'h 8; - parameter logic [BlockAw-1:0] SYSTEM_INFO_SYSTEM_FREQUENCY_OFFSET = 5'h c; - parameter logic [BlockAw-1:0] SYSTEM_INFO_GPIO_INFO_OFFSET = 5'h 10; - parameter logic [BlockAw-1:0] SYSTEM_INFO_UART_INFO_OFFSET = 5'h 14; - parameter logic [BlockAw-1:0] SYSTEM_INFO_I2C_INFO_OFFSET = 5'h 18; - parameter logic [BlockAw-1:0] SYSTEM_INFO_SPI_INFO_OFFSET = 5'h 1c; + parameter logic [BlockAw-1:0] SYSTEM_INFO_RTL_COMMIT_HASH_0_OFFSET = 6'h 0; + parameter logic [BlockAw-1:0] SYSTEM_INFO_RTL_COMMIT_HASH_1_OFFSET = 6'h 4; + parameter logic [BlockAw-1:0] SYSTEM_INFO_RTL_COMMIT_DIRTY_OFFSET = 6'h 8; + parameter logic [BlockAw-1:0] SYSTEM_INFO_SYSTEM_FREQUENCY_OFFSET = 6'h c; + parameter logic [BlockAw-1:0] SYSTEM_INFO_GPIO_INFO_OFFSET = 6'h 10; + parameter logic [BlockAw-1:0] SYSTEM_INFO_UART_INFO_OFFSET = 6'h 14; + parameter logic [BlockAw-1:0] SYSTEM_INFO_I2C_INFO_OFFSET = 6'h 18; + parameter logic [BlockAw-1:0] SYSTEM_INFO_SPI_INFO_OFFSET = 6'h 1c; + parameter logic [BlockAw-1:0] SYSTEM_INFO_DNA_CAPTURE_OFFSET = 6'h 20; + parameter logic [BlockAw-1:0] SYSTEM_INFO_DNA_OFFSET = 6'h 24; + parameter logic [BlockAw-1:0] SYSTEM_INFO_MEM_SIZE_OFFSET = 6'h 28; + parameter logic [BlockAw-1:0] SYSTEM_INFO_HYPERRAM_SIZE_OFFSET = 6'h 2c; + parameter logic [BlockAw-1:0] SYSTEM_INFO_HYPERRAM_TAG_SIZE_OFFSET = 6'h 30; // Reset values for hwext registers and their fields parameter logic [31:0] SYSTEM_INFO_RTL_COMMIT_HASH_0_RESVAL = 32'h 0; @@ -77,6 +118,11 @@ package system_info_reg_pkg; parameter logic [7:0] SYSTEM_INFO_UART_INFO_RESVAL = 8'h 0; parameter logic [7:0] SYSTEM_INFO_I2C_INFO_RESVAL = 8'h 0; parameter logic [7:0] SYSTEM_INFO_SPI_INFO_RESVAL = 8'h 0; + parameter logic [0:0] SYSTEM_INFO_DNA_CAPTURE_RESVAL = 1'h 0; + parameter logic [0:0] SYSTEM_INFO_DNA_RESVAL = 1'h 0; + parameter logic [31:0] SYSTEM_INFO_MEM_SIZE_RESVAL = 32'h 0; + parameter logic [31:0] SYSTEM_INFO_HYPERRAM_SIZE_RESVAL = 32'h 0; + parameter logic [31:0] SYSTEM_INFO_HYPERRAM_TAG_SIZE_RESVAL = 32'h 0; // Register index typedef enum int { @@ -87,19 +133,29 @@ package system_info_reg_pkg; SYSTEM_INFO_GPIO_INFO, SYSTEM_INFO_UART_INFO, SYSTEM_INFO_I2C_INFO, - SYSTEM_INFO_SPI_INFO + SYSTEM_INFO_SPI_INFO, + SYSTEM_INFO_DNA_CAPTURE, + SYSTEM_INFO_DNA, + SYSTEM_INFO_MEM_SIZE, + SYSTEM_INFO_HYPERRAM_SIZE, + SYSTEM_INFO_HYPERRAM_TAG_SIZE } system_info_id_e; // Register width information to check illegal writes - parameter logic [3:0] SYSTEM_INFO_PERMIT [8] = '{ - 4'b 1111, // index[0] SYSTEM_INFO_RTL_COMMIT_HASH_0 - 4'b 1111, // index[1] SYSTEM_INFO_RTL_COMMIT_HASH_1 - 4'b 0001, // index[2] SYSTEM_INFO_RTL_COMMIT_DIRTY - 4'b 1111, // index[3] SYSTEM_INFO_SYSTEM_FREQUENCY - 4'b 0001, // index[4] SYSTEM_INFO_GPIO_INFO - 4'b 0001, // index[5] SYSTEM_INFO_UART_INFO - 4'b 0001, // index[6] SYSTEM_INFO_I2C_INFO - 4'b 0001 // index[7] SYSTEM_INFO_SPI_INFO + parameter logic [3:0] SYSTEM_INFO_PERMIT [13] = '{ + 4'b 1111, // index[ 0] SYSTEM_INFO_RTL_COMMIT_HASH_0 + 4'b 1111, // index[ 1] SYSTEM_INFO_RTL_COMMIT_HASH_1 + 4'b 0001, // index[ 2] SYSTEM_INFO_RTL_COMMIT_DIRTY + 4'b 1111, // index[ 3] SYSTEM_INFO_SYSTEM_FREQUENCY + 4'b 0001, // index[ 4] SYSTEM_INFO_GPIO_INFO + 4'b 0001, // index[ 5] SYSTEM_INFO_UART_INFO + 4'b 0001, // index[ 6] SYSTEM_INFO_I2C_INFO + 4'b 0001, // index[ 7] SYSTEM_INFO_SPI_INFO + 4'b 0001, // index[ 8] SYSTEM_INFO_DNA_CAPTURE + 4'b 0001, // index[ 9] SYSTEM_INFO_DNA + 4'b 1111, // index[10] SYSTEM_INFO_MEM_SIZE + 4'b 1111, // index[11] SYSTEM_INFO_HYPERRAM_SIZE + 4'b 1111 // index[12] SYSTEM_INFO_HYPERRAM_TAG_SIZE }; endpackage diff --git a/rtl/ip/system_info/rtl/system_info_reg_top.sv b/rtl/ip/system_info/rtl/system_info_reg_top.sv index 665dc64e2..cf20073fd 100644 --- a/rtl/ip/system_info/rtl/system_info_reg_top.sv +++ b/rtl/ip/system_info/rtl/system_info_reg_top.sv @@ -10,6 +10,7 @@ module system_info_reg_top ( input clk_i, input rst_ni, // To HW + output system_info_reg_pkg::system_info_reg2hw_t reg2hw, // Write input system_info_reg_pkg::system_info_hw2reg_t hw2reg, // Read input tlul_pkg::tl_h2d_t tl_i, @@ -18,7 +19,7 @@ module system_info_reg_top ( import system_info_reg_pkg::* ; - localparam int AW = 5; + localparam int AW = 6; localparam int DW = 32; localparam int DBW = DW/8; // Byte Width @@ -101,6 +102,16 @@ module system_info_reg_top ( logic [7:0] i2c_info_qs; logic spi_info_re; logic [7:0] spi_info_qs; + logic dna_capture_we; + logic dna_capture_wd; + logic dna_re; + logic dna_qs; + logic mem_size_re; + logic [31:0] mem_size_qs; + logic hyperram_size_re; + logic [31:0] hyperram_size_qs; + logic hyperram_tag_size_re; + logic [31:0] hyperram_tag_size_qs; // Register instances // R[rtl_commit_hash_0]: V(True) @@ -231,18 +242,107 @@ module system_info_reg_top ( ); + // R[dna_capture]: V(True) + logic dna_capture_qe; + logic [0:0] dna_capture_flds_we; + assign dna_capture_qe = &dna_capture_flds_we; + prim_subreg_ext #( + .DW (1) + ) u_dna_capture ( + .re (1'b0), + .we (dna_capture_we), + .wd (dna_capture_wd), + .d ('0), + .qre (), + .qe (dna_capture_flds_we[0]), + .q (reg2hw.dna_capture.q), + .ds (), + .qs () + ); + assign reg2hw.dna_capture.qe = dna_capture_qe; + + + // R[dna]: V(True) + prim_subreg_ext #( + .DW (1) + ) u_dna ( + .re (dna_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.dna.d), + .qre (reg2hw.dna.re), + .qe (), + .q (reg2hw.dna.q), + .ds (), + .qs (dna_qs) + ); + + + // R[mem_size]: V(True) + prim_subreg_ext #( + .DW (32) + ) u_mem_size ( + .re (mem_size_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.mem_size.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (mem_size_qs) + ); + + + // R[hyperram_size]: V(True) + prim_subreg_ext #( + .DW (32) + ) u_hyperram_size ( + .re (hyperram_size_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.hyperram_size.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (hyperram_size_qs) + ); + + + // R[hyperram_tag_size]: V(True) + prim_subreg_ext #( + .DW (32) + ) u_hyperram_tag_size ( + .re (hyperram_tag_size_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.hyperram_tag_size.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (hyperram_tag_size_qs) + ); + + - logic [7:0] addr_hit; + logic [12:0] addr_hit; always_comb begin addr_hit = '0; - addr_hit[0] = (reg_addr == SYSTEM_INFO_RTL_COMMIT_HASH_0_OFFSET); - addr_hit[1] = (reg_addr == SYSTEM_INFO_RTL_COMMIT_HASH_1_OFFSET); - addr_hit[2] = (reg_addr == SYSTEM_INFO_RTL_COMMIT_DIRTY_OFFSET); - addr_hit[3] = (reg_addr == SYSTEM_INFO_SYSTEM_FREQUENCY_OFFSET); - addr_hit[4] = (reg_addr == SYSTEM_INFO_GPIO_INFO_OFFSET); - addr_hit[5] = (reg_addr == SYSTEM_INFO_UART_INFO_OFFSET); - addr_hit[6] = (reg_addr == SYSTEM_INFO_I2C_INFO_OFFSET); - addr_hit[7] = (reg_addr == SYSTEM_INFO_SPI_INFO_OFFSET); + addr_hit[ 0] = (reg_addr == SYSTEM_INFO_RTL_COMMIT_HASH_0_OFFSET); + addr_hit[ 1] = (reg_addr == SYSTEM_INFO_RTL_COMMIT_HASH_1_OFFSET); + addr_hit[ 2] = (reg_addr == SYSTEM_INFO_RTL_COMMIT_DIRTY_OFFSET); + addr_hit[ 3] = (reg_addr == SYSTEM_INFO_SYSTEM_FREQUENCY_OFFSET); + addr_hit[ 4] = (reg_addr == SYSTEM_INFO_GPIO_INFO_OFFSET); + addr_hit[ 5] = (reg_addr == SYSTEM_INFO_UART_INFO_OFFSET); + addr_hit[ 6] = (reg_addr == SYSTEM_INFO_I2C_INFO_OFFSET); + addr_hit[ 7] = (reg_addr == SYSTEM_INFO_SPI_INFO_OFFSET); + addr_hit[ 8] = (reg_addr == SYSTEM_INFO_DNA_CAPTURE_OFFSET); + addr_hit[ 9] = (reg_addr == SYSTEM_INFO_DNA_OFFSET); + addr_hit[10] = (reg_addr == SYSTEM_INFO_MEM_SIZE_OFFSET); + addr_hit[11] = (reg_addr == SYSTEM_INFO_HYPERRAM_SIZE_OFFSET); + addr_hit[12] = (reg_addr == SYSTEM_INFO_HYPERRAM_TAG_SIZE_OFFSET); end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; @@ -250,14 +350,19 @@ module system_info_reg_top ( // Check sub-word write is permitted always_comb begin wr_err = (reg_we & - ((addr_hit[0] & (|(SYSTEM_INFO_PERMIT[0] & ~reg_be))) | - (addr_hit[1] & (|(SYSTEM_INFO_PERMIT[1] & ~reg_be))) | - (addr_hit[2] & (|(SYSTEM_INFO_PERMIT[2] & ~reg_be))) | - (addr_hit[3] & (|(SYSTEM_INFO_PERMIT[3] & ~reg_be))) | - (addr_hit[4] & (|(SYSTEM_INFO_PERMIT[4] & ~reg_be))) | - (addr_hit[5] & (|(SYSTEM_INFO_PERMIT[5] & ~reg_be))) | - (addr_hit[6] & (|(SYSTEM_INFO_PERMIT[6] & ~reg_be))) | - (addr_hit[7] & (|(SYSTEM_INFO_PERMIT[7] & ~reg_be))))); + ((addr_hit[ 0] & (|(SYSTEM_INFO_PERMIT[ 0] & ~reg_be))) | + (addr_hit[ 1] & (|(SYSTEM_INFO_PERMIT[ 1] & ~reg_be))) | + (addr_hit[ 2] & (|(SYSTEM_INFO_PERMIT[ 2] & ~reg_be))) | + (addr_hit[ 3] & (|(SYSTEM_INFO_PERMIT[ 3] & ~reg_be))) | + (addr_hit[ 4] & (|(SYSTEM_INFO_PERMIT[ 4] & ~reg_be))) | + (addr_hit[ 5] & (|(SYSTEM_INFO_PERMIT[ 5] & ~reg_be))) | + (addr_hit[ 6] & (|(SYSTEM_INFO_PERMIT[ 6] & ~reg_be))) | + (addr_hit[ 7] & (|(SYSTEM_INFO_PERMIT[ 7] & ~reg_be))) | + (addr_hit[ 8] & (|(SYSTEM_INFO_PERMIT[ 8] & ~reg_be))) | + (addr_hit[ 9] & (|(SYSTEM_INFO_PERMIT[ 9] & ~reg_be))) | + (addr_hit[10] & (|(SYSTEM_INFO_PERMIT[10] & ~reg_be))) | + (addr_hit[11] & (|(SYSTEM_INFO_PERMIT[11] & ~reg_be))) | + (addr_hit[12] & (|(SYSTEM_INFO_PERMIT[12] & ~reg_be))))); end // Generate write-enables @@ -269,6 +374,13 @@ module system_info_reg_top ( assign uart_info_re = addr_hit[5] & reg_re & !reg_error; assign i2c_info_re = addr_hit[6] & reg_re & !reg_error; assign spi_info_re = addr_hit[7] & reg_re & !reg_error; + assign dna_capture_we = addr_hit[8] & reg_we & !reg_error; + + assign dna_capture_wd = reg_wdata[0]; + assign dna_re = addr_hit[9] & reg_re & !reg_error; + assign mem_size_re = addr_hit[10] & reg_re & !reg_error; + assign hyperram_size_re = addr_hit[11] & reg_re & !reg_error; + assign hyperram_tag_size_re = addr_hit[12] & reg_re & !reg_error; // Read data return always_comb begin @@ -306,6 +418,26 @@ module system_info_reg_top ( reg_rdata_next[7:0] = spi_info_qs; end + addr_hit[8]: begin + reg_rdata_next[0] = '0; + end + + addr_hit[9]: begin + reg_rdata_next[0] = dna_qs; + end + + addr_hit[10]: begin + reg_rdata_next[31:0] = mem_size_qs; + end + + addr_hit[11]: begin + reg_rdata_next[31:0] = hyperram_size_qs; + end + + addr_hit[12]: begin + reg_rdata_next[31:0] = hyperram_tag_size_qs; + end + default: begin reg_rdata_next = '1; end diff --git a/rtl/ip/system_info/templates/system_info.sv.tpl b/rtl/ip/system_info/templates/system_info.sv.tpl index 315934823..728c1e490 100644 --- a/rtl/ip/system_info/templates/system_info.sv.tpl +++ b/rtl/ip/system_info/templates/system_info.sv.tpl @@ -6,17 +6,20 @@ // For example the commit hash from which this bitstream was generated. // This file has been auto-generated, please edit the file in the rtl/templates folder. // Please do not commit a generated SystemVerilog version of this file with an actual git hash. -// The value in the git hash commited to the repository should be equal to all zeroes. +// The value in the git hash committed to the repository should be equal to all zeros. // Only when generating a release, the top generator can be used to insert the final hash into the system. // To do this use the command: // ./util/top_gen.py system_info module system_info import system_info_reg_pkg::*; #( - parameter int unsigned SysClkFreq = 0, - parameter int unsigned GpioNum = 0, - parameter int unsigned UartNum = 0, - parameter int unsigned I2cNum = 0, - parameter int unsigned SpiNum = 0 + parameter int unsigned SysClkFreq = 0, + parameter int unsigned GpioNum = 0, + parameter int unsigned UartNum = 0, + parameter int unsigned I2cNum = 0, + parameter int unsigned SpiNum = 0, + parameter int unsigned MemSize = 0, + parameter int unsigned HyperRAMSize = 0, + parameter int unsigned HyperRAMTagSize = 0 ) ( // Clock and reset. input logic clk_i, @@ -26,8 +29,21 @@ module system_info import system_info_reg_pkg::*; #( input tlul_pkg::tl_h2d_t tl_i, output tlul_pkg::tl_d2h_t tl_o ); + system_info_reg2hw_t reg2hw; system_info_hw2reg_t hw2reg; + // Return the 57-bit 'most often unique' DNA value of the device, in a bit-serial manner. + wire dna_read = reg2hw.dna_capture.qe; // Any write (re-)reads the DNA value. + wire dna_shift = reg2hw.dna.re; // Advance to the next bit. + logic dna_dout; + DNA_PORT u_dna( + .CLK (clk_i), + .READ (dna_read), + .SHIFT (dna_shift), + .DOUT (dna_dout), + .DIN (1'b0) + ); + assign hw2reg.rtl_commit_hash_0.d = 'h${system_info.commit_hash[0:8]}; assign hw2reg.rtl_commit_hash_1.d = 'h${system_info.commit_hash[8:16]}; assign hw2reg.rtl_commit_dirty.d = 'b${'1' if system_info.dirty else '0'}; @@ -36,6 +52,10 @@ module system_info import system_info_reg_pkg::*; #( assign hw2reg.uart_info.d = 8'(UartNum); assign hw2reg.i2c_info.d = 8'(I2cNum); assign hw2reg.spi_info.d = 8'(SpiNum); + assign hw2reg.dna.d = dna_dout; + assign hw2reg.mem_size.d = top_pkg::TL_DW'(MemSize >> 10); + assign hw2reg.hyperram_size.d = top_pkg::TL_DW'(HyperRAMSize >> 10); + assign hw2reg.hyperram_tag_size.d = top_pkg::TL_DW'(HyperRAMTagSize >> 10); // Instantiate the registers system_info_reg_top u_system_info_reg_top ( @@ -43,6 +63,7 @@ module system_info import system_info_reg_pkg::*; #( .rst_ni, .tl_i, .tl_o, - .hw2reg + .hw2reg, + .reg2hw ); endmodule diff --git a/rtl/system/sonata_system.sv b/rtl/system/sonata_system.sv index 5273a718b..f070f5af1 100644 --- a/rtl/system/sonata_system.sv +++ b/rtl/system/sonata_system.sv @@ -1216,11 +1216,14 @@ module sonata_system assign host_wcap[DbgHost] = 1'b0; system_info #( - .SysClkFreq ( SysClkFreq ), - .GpioNum ( TotalGpioNum ), - .UartNum ( UART_NUM ), - .I2cNum ( I2C_NUM ), - .SpiNum ( TotalSpiNum ) + .SysClkFreq ( SysClkFreq ), + .GpioNum ( TotalGpioNum ), + .UartNum ( UART_NUM ), + .I2cNum ( I2C_NUM ), + .SpiNum ( TotalSpiNum ), + .MemSize ( MemSize ), + .HyperRAMSize ( HyperRAMSize ), + .HyperRAMTagSize ( HyperRAMTagSize ) ) u_system_info ( .clk_i (clk_sys_i), .rst_ni (rst_sys_ni), diff --git a/sonata.core b/sonata.core index 6f91f14bb..6ac8a6c8b 100644 --- a/sonata.core +++ b/sonata.core @@ -55,6 +55,7 @@ filesets: - dv/dpi/spidpi/spi_microsd.cc: { file_type: cppSource } - dv/dpi/spidpi/spi_microsd.hh: { file_type: cppSource, is_include_file: true } - dv/models/hyperram/rtl/hyperram_W956.sv: { file_type: systemVerilogSource } + - dv/models/fpga/rtl/DNA_PORT.v: { file_type: systemVerilogSource } - dv/models/fpga/rtl/IOBUF.v: { file_type: systemVerilogSource } - dv/models/fpga/rtl/ISERDESE2.v: { file_type: systemVerilogSource } - dv/models/fpga/rtl/OBUF.v: { file_type: systemVerilogSource } diff --git a/sw/cheri/checks/dma_check.cc b/sw/cheri/checks/dma_check.cc new file mode 100644 index 000000000..6463c0c3e --- /dev/null +++ b/sw/cheri/checks/dma_check.cc @@ -0,0 +1,114 @@ +/** + * Copyright lowRISC contributors. + * Licensed under the Apache License, Version 2.0, see LICENSE for details. + * SPDX-License-Identifier: Apache-2.0 + */ +#define CHERIOT_NO_AMBIENT_MALLOC +#define CHERIOT_NO_NEW_DELETE + +#include + +#include +// clang-format off +#include "../../common/defs.h" +// clang-format on +#include "../common/console.hh" +#include "../common/timer-utils.hh" + +using namespace CHERI; + +struct SonataDma { + /** + * Interrupt State Register. + */ + uint32_t interruptState; + /** + * Interrupt Enable Register. + */ + uint32_t interruptEnable; + /** + * Interrupt Test Register. + */ + uint32_t interruptTest; + + // TODO: Under construction. + uint32_t control; + uint32_t status; + + uint32_t src_config; + // TODO: Will need aligning. + uint32_t src_cap_lo; + uint32_t src_cap_hi; + uint32_t src_stride; + uint32_t src_row_len; + uint32_t src_rows; + + uint32_t dst_config; + // TODO: Will need aligning. + uint32_t dst_cap_lo; + uint32_t dst_cap_hi; + uint32_t dst_stride; + uint32_t dst_row_len; + uint32_t dst_rows; +}; + +/** + * C++ entry point for the loader. This is called from assembly, with the + * read-write root in the first argument. + */ +extern "C" uint32_t entry_point(void *rwRoot) { + Capability root{rwRoot}; + + // Create a bounded capability to the UART + Capability uart0 = root.cast(); + uart0.address() = UART_ADDRESS; + uart0.bounds() = UART_BOUNDS; + + uart0->init(BAUD_RATE); + WriteUart uart{uart0}; + Log log(uart); + + // Create a bounded capability to the UART + Capability dma = root.cast(); + dma.address() = DMA_ADDRESS; + dma.bounds() = DMA_BOUNDS; + + while (true) { + const bool logging = false; + + // Set up a simple copy from SRAM to HyperRAM + // 8 rows x 0x400 words/row = 8192 words to be transferred. + dma->src_config = 0x11u; + dma->dst_config = 0x11u; + + dma->src_cap_lo = SRAM_ADDRESS; + dma->src_cap_hi = 0u; + dma->src_stride = 0x404; + dma->src_rows = 0x7; + dma->src_row_len = 0x3ff; + + dma->dst_cap_lo = HYPERRAM_ADDRESS; + dma->dst_cap_hi = 0u; + dma->dst_stride = 0x804; + dma->dst_rows = 0x3; + dma->dst_row_len = 0x7ff; + + log.println("Starting transfer"); + uint32_t start_time = get_mcycle(); + dma->control = 1; + + int cnt = 0; + if (logging) { + log.println("Started, state {}", dma->status & 0xfu); + } + while (dma->status & 0xfu) { + asm(""); + if (++cnt < 10) + if (logging) { + log.println(" - status {:#x}", dma->status); + } + } + + log.println("Took {} cycles", get_mcycle() - start_time); + } +} diff --git a/sw/cheri/checks/system_info_check.cc b/sw/cheri/checks/system_info_check.cc index 12e2e1457..3fdc5512d 100644 --- a/sw/cheri/checks/system_info_check.cc +++ b/sw/cheri/checks/system_info_check.cc @@ -19,6 +19,16 @@ using namespace CHERI; +// Read the 'most often unique' +static void dna_read(Capability sysinfo, uint64_t& dna) { + // Initiate reading of the DNA value. + sysinfo[8] = 1; + // The DNA value is 57 bits in length and is read bit-serially. + for (unsigned b = 0u; b < 57; ++b) { + dna = (dna << 1) | (sysinfo[9] & 1u); + } +} + /** * C++ entry point for the loader. This is called from assembly, with the * read-write root in the first argument. @@ -32,18 +42,25 @@ extern "C" [[noreturn]] void entry_point(void* rwRoot) { uart.bounds() = UART_BOUNDS; // Create bounded capability to system info. - Capability sysinfo = root.cast(); - sysinfo.address() = SYSTEM_INFO_ADDRESS; - sysinfo.bounds() = SYSTEM_INFO_BOUNDS; - - uint32_t git_hash_0 = sysinfo[0]; - uint32_t git_hash_1 = sysinfo[1]; - uint32_t git_dirty = sysinfo[2]; - uint32_t system_frequency = sysinfo[3]; - uint32_t gpio_info = sysinfo[4]; - uint32_t uart_info = sysinfo[5]; - uint32_t i2c_info = sysinfo[6]; - uint32_t spi_info = sysinfo[7]; + Capability sysinfo = root.cast(); + sysinfo.address() = SYSTEM_INFO_ADDRESS; + sysinfo.bounds() = SYSTEM_INFO_BOUNDS; + + uint32_t git_hash_0 = sysinfo[0]; + uint32_t git_hash_1 = sysinfo[1]; + uint32_t git_dirty = sysinfo[2]; + uint32_t system_frequency = sysinfo[3]; + uint32_t gpio_info = sysinfo[4]; + uint32_t uart_info = sysinfo[5]; + uint32_t i2c_info = sysinfo[6]; + uint32_t spi_info = sysinfo[7]; + uint32_t mem_size = sysinfo[10]; + uint32_t hyperram_size = sysinfo[11]; + uint32_t hyperram_tag_size = sysinfo[12]; + + // Reading the 'DNA value' of the FPGA is more involved than a simple register read. + uint64_t dna; + dna_read(sysinfo, dna); uart->init(BAUD_RATE); write_str(uart, "Hash is equal to:\r\n"); @@ -77,6 +94,23 @@ extern "C" [[noreturn]] void entry_point(void* rwRoot) { write_hex(uart, spi_info); write_str(uart, "\r\n"); + write_str(uart, "DNA: 0x"); + write_hex(uart, (uint32_t)(dna >> 32)); + write_hex(uart, (uint32_t)dna); + write_str(uart, "\r\n"); + + write_str(uart, "Mem size: 0x"); + write_hex(uart, mem_size); + write_str(uart, "KiB\r\n"); + + write_str(uart, "HyperRAM size: 0x"); + write_hex(uart, hyperram_size); + write_str(uart, "KiB\r\n"); + + write_str(uart, "Tagged HyperRAM size: 0x"); + write_hex(uart, hyperram_tag_size); + write_str(uart, "KiB\r\n"); + char ch = '\n'; while (true) { ch = uart->blocking_read(); diff --git a/sw/common/defs.h b/sw/common/defs.h index 94fee569a..7822df2c2 100644 --- a/sw/common/defs.h +++ b/sw/common/defs.h @@ -18,7 +18,7 @@ #define HYPERRAM_TAG_BOUNDS (0x0040'0000) #define SYSTEM_INFO_ADDRESS (0x8000'C000) -#define SYSTEM_INFO_BOUNDS (0x0000'0020) +#define SYSTEM_INFO_BOUNDS (0x0000'0034) #define GPIO_NUM 6 #define GPIO_ADDRESS (0x8000'0000)