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/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/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/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 793760203..f070f5af1 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), @@ -1214,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/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/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/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. 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..7822df2c2 100644 --- a/sw/common/defs.h +++ b/sw/common/defs.h @@ -13,10 +13,12 @@ #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) +#define SYSTEM_INFO_BOUNDS (0x0000'0034) #define GPIO_NUM 6 #define GPIO_ADDRESS (0x8000'0000)