From a225f40cbbc64417d9ef3ab5345ce1c4b76ef48c Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:36:03 +0200 Subject: [PATCH 01/37] apb_intf: Update stale header --- src/apb_intf.sv | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index 70a659c..7fd5044 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -1,4 +1,5 @@ -// Copyright 2014-2018 ETH Zurich and University of Bologna. +// Copyright (c) 2014 ETH Zurich, University of Bologna +// // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at @@ -7,11 +8,8 @@ // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -// -// Fabian Schuiki -// -// This file defines the interfaces we support. +// An APB2 interface interface APB_BUS #( parameter int unsigned APB_ADDR_WIDTH = 32, parameter int unsigned APB_DATA_WIDTH = 32 From ade771ddb1be142f9652a07f49126b2397ac9c1b Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:38:14 +0200 Subject: [PATCH 02/37] apb_intf: Rename interface `APB_BUS` to `APB` The 'B' in 'APB' stands for bus, so there is no need for that suffix. --- src/apb_intf.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index 7fd5044..6c3ee1e 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -10,7 +10,8 @@ // specific language governing permissions and limitations under the License. // An APB2 interface -interface APB_BUS #( +interface APB #( +interface APB; parameter int unsigned APB_ADDR_WIDTH = 32, parameter int unsigned APB_DATA_WIDTH = 32 ); From 6a9d29d3f213870b135613fa5fbbd096a3418e45 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:39:44 +0200 Subject: [PATCH 03/37] apb_intf: Turn parameters into constants Both address and data signals can be up to 32 bit wide. Due to the simple structure of APB, we can assume a reasonably good synthesizer optimizes away unused parts of these signals, so it does not hurt to simply fix them at the maximum value. Besides, this saves problems with unsupported interface parametrization in some tools and obviates the need for data and address width converters. --- src/apb_intf.sv | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index 6c3ee1e..fa6c950 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -10,20 +10,27 @@ // specific language governing permissions and limitations under the License. // An APB2 interface -interface APB #( interface APB; - parameter int unsigned APB_ADDR_WIDTH = 32, - parameter int unsigned APB_DATA_WIDTH = 32 -); - - logic [APB_ADDR_WIDTH-1:0] paddr; - logic [APB_DATA_WIDTH-1:0] pwdata; - logic pwrite; - logic psel; - logic penable; - logic [APB_DATA_WIDTH-1:0] prdata; - logic pready; - logic pslverr; + + localparam ADDR_WIDTH = 32; + localparam DATA_WIDTH = 32; + localparam STRB_WIDTH = DATA_WIDTH / 8; + + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [2:0] prot_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + + addr_t paddr; + prot_t pprot; + logic psel; + logic penable; + logic pwrite; + data_t pwdata; + strb_t pstrb; + logic pready; + data_t prdata; + logic pslverr; // Master Side From c131a85de9e37222f9d64014fea0a28a5cff5c4b Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:46:28 +0200 Subject: [PATCH 04/37] apb_intf: Remove redundant `in` and `out` modports Modport `in` was an alias of `Slave` and `out` was an alias of `Master`. However, many tools do not recognize such aliases as being identical, causing problems when feeding a `Master` to a `out` (and vice-versa). To prevent these problems, the redundant `in` and `out` modports have been removed. --- src/apb_intf.sv | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index fa6c950..601a0d9 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -32,30 +32,14 @@ interface APB; data_t prdata; logic pslverr; - - // Master Side - modport Master ( - output paddr, pwdata, pwrite, psel, penable, - input prdata, pready, pslverr - ); - - // Slave Side - modport Slave ( - input paddr, pwdata, pwrite, psel, penable, - output prdata, pready, pslverr - ); - - /// The interface as an output (issuing requests, initiator, master). - modport out ( - output paddr, pwdata, pwrite, psel, penable, - input prdata, pready, pslverr - ); - - /// The interface as an input (accepting requests, target, slave) - modport in ( - input paddr, pwdata, pwrite, psel, penable, - output prdata, pready, pslverr - ); - + modport Master ( + output paddr, pprot, psel, penable, pwrite, pwdata, pstrb, + input pready, prdata, pslverr + ); + + modport Slave ( + input paddr, pprot, psel, penable, pwrite, pwdata, pstrb, + output pready, prdata, pslverr + ); endinterface From 4e6385e3e83563a4d9735406112ded2fbb5bdcb9 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:47:16 +0200 Subject: [PATCH 05/37] apb_intf: Add clocked interface for design verification --- src/apb_intf.sv | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index 601a0d9..0f14b76 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -43,3 +43,40 @@ interface APB; ); endinterface + +// A clocked APB2 interface for use in design verification +interface APB_DV ( + input logic clk_i +); + + localparam ADDR_WIDTH = 32; + localparam DATA_WIDTH = 32; + localparam STRB_WIDTH = DATA_WIDTH / 8; + + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [2:0] prot_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + + addr_t paddr; + prot_t pprot; + logic psel; + logic penable; + logic pwrite; + data_t pwdata; + strb_t pstrb; + logic pready; + data_t prdata; + logic pslverr; + + modport Master ( + output paddr, pprot, psel, penable, pwrite, pwdata, pstrb, + input pready, prdata, pslverr + ); + + modport Slave ( + input paddr, pprot, psel, penable, pwrite, pwdata, pstrb, + output pready, prdata, pslverr + ); + +endinterface From 11636e13027c9ddbb22a6c0fd1488e6cd111e0b7 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:48:01 +0200 Subject: [PATCH 06/37] Define macros for assigning APB interfaces --- Bender.yml | 3 +++ include/apb/assign.svh | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 include/apb/assign.svh diff --git a/Bender.yml b/Bender.yml index 16b791c..e23332f 100644 --- a/Bender.yml +++ b/Bender.yml @@ -2,5 +2,8 @@ package: name: apb authors: ["Fabian Schuiki "] +export_include_dirs: + - include + sources: - src/apb_intf.sv diff --git a/include/apb/assign.svh b/include/apb/assign.svh new file mode 100644 index 0000000..d73c37e --- /dev/null +++ b/include/apb/assign.svh @@ -0,0 +1,28 @@ +// Copyright (c) 2018 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +`ifndef APB_ASSIGN_SVH_ +`define APB_ASSIGN_SVH_ + +// Assign an APB2 master interface to a slave interface, as in `assign slv = mst;`. +`define APB_ASSIGN(slv, mst) \ + assign slv.paddr = mst.paddr; \ + assign slv.pprot = mst.pprot; \ + assign slv.psel = mst.psel; \ + assign slv.penable = mst.penable; \ + assign slv.pwrite = mst.pwrite; \ + assign slv.pwdata = mst.pwdata; \ + assign slv.pstrb = mst.pstrb; \ + assign mst.pready = slv.pready; \ + assign mst.prdata = slv.prdata; \ + assign mst.pslverr = slv.pslverr; + +`endif From 4d47d5807ace457bba48c40a92bb344f160fd9fd Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:50:45 +0200 Subject: [PATCH 07/37] Bender: Update authors --- Bender.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Bender.yml b/Bender.yml index e23332f..783f4a7 100644 --- a/Bender.yml +++ b/Bender.yml @@ -1,6 +1,8 @@ package: name: apb - authors: ["Fabian Schuiki "] + authors: + - "Andreas Kurth " # current maintainer + - "Fabian Schuiki " export_include_dirs: - include From 035904bbc451918b693d54bd0b1363154a3ff83c Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:51:53 +0200 Subject: [PATCH 08/37] Bender: Define structure of `sources` --- Bender.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Bender.yml b/Bender.yml index 783f4a7..aaba243 100644 --- a/Bender.yml +++ b/Bender.yml @@ -8,4 +8,8 @@ export_include_dirs: - include sources: + # Source files grouped in levels. Files in level 0 have no dependencies on files in this + # package. Files in level 1 only depend on files in level 0, files in level 2 on files in + # levels 1 and 0, etc. Files within a level are ordered alphabetically. + # Level 0 - src/apb_intf.sv From 58ddabbbf0f22553e2edc5fb479f87c48eae1342 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:53:13 +0200 Subject: [PATCH 09/37] Add APB bus with single master and multiple slave interfaces --- Bender.yml | 5 ++ src/apb_bus.sv | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 src/apb_bus.sv diff --git a/Bender.yml b/Bender.yml index aaba243..489c553 100644 --- a/Bender.yml +++ b/Bender.yml @@ -4,6 +4,9 @@ package: - "Andreas Kurth " # current maintainer - "Fabian Schuiki " +dependencies: + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.10.0 } + export_include_dirs: - include @@ -13,3 +16,5 @@ sources: # levels 1 and 0, etc. Files within a level are ordered alphabetically. # Level 0 - src/apb_intf.sv + # Level 1 + - src/apb_bus.sv diff --git a/src/apb_bus.sv b/src/apb_bus.sv new file mode 100644 index 0000000..c28562b --- /dev/null +++ b/src/apb_bus.sv @@ -0,0 +1,122 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// APB Bus with Single Master and Multiple Slave Interfaces +// Each slave is accessible at exactly one address segment, and the address ranges of no two slaves +// may overlap. If the input address of this bus is not in any slave address segment, the bus +// responds with slverr; otherwise, this bus feeds all signals through to the slave. The slaves see +// offset-compensated addresses, e.g., if this bus gets ADDR_BEGIN[i] as input address, slave i will +// get address 0. +module apb_bus #( + // Number of slaves. + parameter int unsigned N_SLV = 0, + // Address ranges of the slaves. Slave i is mapped in the inclusive interval from ADDR_BEGIN[i] to + // ADDR_END[i]. + parameter logic [N_SLV-1:0][31:0] ADDR_BEGIN = '0, + parameter logic [N_SLV-1:0][31:0] ADDR_END = '0 +) ( + // Input + input logic pclk_i, + input logic preset_ni, + input logic [31:0] paddr_i, + input logic [2:0] pprot_i, + input logic psel_i, + input logic penable_i, + input logic pwrite_i, + input logic [31:0] pwdata_i, + input logic [3:0] pstrb_i, + output logic pready_o, + output logic [31:0] prdata_o, + output logic pslverr_o, + + // Outputs + output logic [N_SLV-1:0] pclk_o, + output logic [N_SLV-1:0] preset_no, + output logic [N_SLV-1:0][31:0] paddr_o, + output logic [N_SLV-1:0] [2:0] pprot_o, + output logic [N_SLV-1:0] psel_o, + output logic [N_SLV-1:0] penable_o, + output logic [N_SLV-1:0] pwrite_o, + output logic [N_SLV-1:0][31:0] pwdata_o, + output logic [N_SLV-1:0] [3:0] pstrb_o, + input logic [N_SLV-1:0] pready_i, + input logic [N_SLV-1:0][31:0] prdata_i, + input logic [N_SLV-1:0] pslverr_i +); + + logic [$clog2(N_SLV)-1:0] sel_idx; + logic dec_err; + + for (genvar i = 0; i < N_SLV; i++) begin: gen_oup_demux + assign pclk_o[i] = pclk_i; + assign preset_no[i] = preset_ni; + assign paddr_o[i] = paddr_i - ADDR_BEGIN[i]; + assign pprot_o[i] = pprot_i; + assign psel_o[i] = psel_i & (paddr_i >= ADDR_BEGIN[i] && paddr_i <= ADDR_END[i]); + assign penable_o[i] = penable_i; + assign pwrite_o[i] = pwrite_i; + assign pwdata_o[i] = pwdata_i; + assign pstrb_o[i] = pstrb_i; + end + + assign dec_err = psel_i & ~(|psel_o); + + onehot_to_bin #(.ONEHOT_WIDTH(N_SLV)) i_sel_idx ( + .onehot (psel_o), + .bin (sel_idx) + ); + + always_comb begin + if (psel_i) begin + if (dec_err) begin + pready_o = 1'b1; + prdata_o = '0; + pslverr_o = 1'b1; + end else begin + pready_o = pready_i[sel_idx]; + prdata_o = prdata_i[sel_idx]; + pslverr_o = pslverr_i[sel_idx]; + end + end else begin // !psel_i + pready_o = 1'b0; + prdata_o = '0; + pslverr_o = 1'b0; + end + end + +// Validate parameters. +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (N_SLV >= 1) else $fatal(1, "The number of slave ports must be at least 1!"); + end + for (genvar i = 0; i < N_SLV; i++) begin: gen_assert_addr_outer + initial begin + assert (ADDR_BEGIN[i] <= ADDR_END[i]) + else $fatal(1, "Invalid address range for slave %0d", i); + end + for (genvar j = 0; j < N_SLV; j++) begin: gen_assert_addr_inner + initial begin + if (i != j) begin + if (ADDR_BEGIN[j] >= ADDR_BEGIN[i]) begin + assert (ADDR_BEGIN[j] > ADDR_END[i]) + else $fatal("Address range of slaves %0d and %0d overlap!", i, j); + end else begin + assert (ADDR_END[j] < ADDR_BEGIN[i]) + else $fatal("Address range of slaves %0d and %0d overlap!", i, j); + end + end + end + end + end +`endif +// pragma translate_on + +endmodule From c31a9d624dd6b734da731b25090d9360c0f4a75e Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:54:09 +0200 Subject: [PATCH 10/37] Add APB read-only registers --- Bender.yml | 1 + src/apb_ro_regs.sv | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/apb_ro_regs.sv diff --git a/Bender.yml b/Bender.yml index 489c553..b9bca23 100644 --- a/Bender.yml +++ b/Bender.yml @@ -18,3 +18,4 @@ sources: - src/apb_intf.sv # Level 1 - src/apb_bus.sv + - src/apb_ro_regs.sv diff --git a/src/apb_ro_regs.sv b/src/apb_ro_regs.sv new file mode 100644 index 0000000..ed2d4ef --- /dev/null +++ b/src/apb_ro_regs.sv @@ -0,0 +1,64 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// APB Read-Only Registers +// This module exposes a number of 32-bit registers (provided on the `reg_i` input) read-only on an +// APB interface. It responds to reads that are out of range and writes with a slave error. +module apb_ro_regs #( + parameter int unsigned N_REGS = 0 +) ( + // APB Interface + input logic pclk_i, + input logic preset_ni, + input logic [31:0] paddr_i, + input logic [2:0] pprot_i, + input logic psel_i, + input logic penable_i, + input logic pwrite_i, + input logic [31:0] pwdata_i, + input logic [3:0] pstrb_i, + output logic pready_o, + output logic [31:0] prdata_o, + output logic pslverr_o, + + // Register Interface + input logic [N_REGS-1:0][31:0] reg_i +); + + always_comb begin + prdata_o = 'x; + pslverr_o = 1'b0; + if (psel_i) begin + if (pwrite_i) begin + // Error response to writes + pslverr_o = 1'b1; + end else begin + automatic logic [29:0] word_addr = paddr_i >> 2; + if (word_addr >= N_REGS) begin + // Error response to reads out of range + pslverr_o = 1'b1; + end else begin + prdata_o = reg_i[word_addr]; + end + end + end + end + assign pready_o = psel_i & penable_i; + +// Validate parameters. +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (N_REGS >= 1) else $fatal(1, "The number of registers must be at least 1!"); + end +`endif +// pragma translate_on + +endmodule From 44b1604ae8ef53916b0ab8bde6fdaaa5dbec724d Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:54:36 +0200 Subject: [PATCH 11/37] Add APB read-write registers --- Bender.yml | 1 + src/apb_rw_regs.sv | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/apb_rw_regs.sv diff --git a/Bender.yml b/Bender.yml index b9bca23..99a0342 100644 --- a/Bender.yml +++ b/Bender.yml @@ -19,3 +19,4 @@ sources: # Level 1 - src/apb_bus.sv - src/apb_ro_regs.sv + - src/apb_rw_regs.sv diff --git a/src/apb_rw_regs.sv b/src/apb_rw_regs.sv new file mode 100644 index 0000000..eb289d0 --- /dev/null +++ b/src/apb_rw_regs.sv @@ -0,0 +1,81 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// APB Read-Write Registers +// This module exposes a number of 32-bit registers (provided on the `reg_i` input) writable on an +// APB interface. It responds to accesses that are out of range with a slave error. +module apb_rw_regs #( + parameter int unsigned N_REGS = 0 +) ( + // APB Interface + input logic pclk_i, + input logic preset_ni, + input logic [31:0] paddr_i, + input logic [2:0] pprot_i, + input logic psel_i, + input logic penable_i, + input logic pwrite_i, + input logic [31:0] pwdata_i, + input logic [3:0] pstrb_i, + output logic pready_o, + output logic [31:0] prdata_o, + output logic pslverr_o, + + // Register Interface + input logic [N_REGS-1:0][31:0] init_i, + output logic [N_REGS-1:0][31:0] q_o +); + + logic [N_REGS-1:0][31:0] reg_d, reg_q; + + always_comb begin + reg_d = reg_q; + prdata_o = 'x; + pslverr_o = 1'b0; + if (psel_i) begin + automatic logic [29:0] word_addr = paddr_i >> 2; + if (word_addr >= N_REGS) begin + // Error response to accesses that are out of range + pslverr_o = 1'b1; + end else begin + if (pwrite_i) begin + for (int i = 0; i < 4; i++) begin + if (pstrb_i[i]) begin + reg_d[word_addr][i*8 +: 8] = pwdata_i[i*8 +: 8]; + end + end + end else begin + prdata_o = reg_q[word_addr]; + end + end + end + end + assign pready_o = psel_i & penable_i; + + assign q_o = reg_q; + + always_ff @(posedge pclk_i or negedge preset_ni) begin + if (!preset_ni) begin + reg_q <= init_i; + end else begin + reg_q <= reg_d; + end + end + +// Validate parameters. +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (N_REGS >= 1) else $fatal(1, "The number of registers must be at least 1!"); + end +`endif +// pragma translate_on + +endmodule From e454aef5fa8f3733a4fb7d12d73b7afab2bba78c Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:55:05 +0200 Subject: [PATCH 12/37] Add basic test infrastructure for APB modules --- Bender.yml | 4 ++ src/apb_test.sv | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/apb_test.sv diff --git a/Bender.yml b/Bender.yml index 99a0342..efcd84a 100644 --- a/Bender.yml +++ b/Bender.yml @@ -20,3 +20,7 @@ sources: - src/apb_bus.sv - src/apb_ro_regs.sv - src/apb_rw_regs.sv + + - target: simulation + files: + - src/apb_test.sv diff --git a/src/apb_test.sv b/src/apb_test.sv new file mode 100644 index 0000000..7feaaec --- /dev/null +++ b/src/apb_test.sv @@ -0,0 +1,114 @@ +// Copyright (c) 2018 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Test infrastructure for APB interfaces +package apb_test; + + // Simple APB driver with thread-safe 32-bit read and write functions + class apb_driver #( + parameter time TA = 0ns, + parameter time TT = 0ns + ); + + virtual APB_DV apb; + semaphore lock; + + function new(virtual APB_DV apb); + this.apb = apb; + this.lock = new(1); + endfunction + + function void reset_master(); + apb.paddr <= '0; + apb.pprot <= '0; + apb.psel <= 1'b0; + apb.penable <= 1'b0; + apb.pwrite <= 1'b0; + apb.pwdata <= '0; + apb.pstrb <= '0; + endfunction + + function void reset_slave(); + apb.pready <= 1'b0; + apb.prdata <= '0; + apb.pslverr <= 1'b0; + endfunction + + task cycle_start; + #TT; + endtask + + task cycle_end; + @(posedge apb.clk_i); + endtask + + task read( + input logic [31:0] addr, + output logic [31:0] data, + output logic err + ); + while (!lock.try_get()) begin + cycle_end(); + end + apb.paddr <= #TA addr; + apb.pwrite <= #TA 1'b0; + apb.psel <= #TA 1'b1; + cycle_end(); + apb.penable <= #TA 1'b1; + cycle_start(); + while (!apb.pready) begin + cycle_end(); + cycle_start(); + end + data = apb.prdata; + err = apb.pslverr; + cycle_end(); + apb.paddr <= #TA '0; + apb.psel <= #TA 1'b0; + apb.penable <= #TA 1'b0; + lock.put(); + endtask + + task write( + input logic [31:0] addr, + input logic [31:0] data, + input logic [3:0] strb, + output logic err + ); + while (!lock.try_get()) begin + cycle_end(); + end + apb.paddr <= #TA addr; + apb.pwdata <= #TA data; + apb.pstrb <= #TA strb; + apb.pwrite <= #TA 1'b1; + apb.psel <= #TA 1'b1; + cycle_end(); + apb.penable <= #TA 1'b1; + cycle_start(); + while (!apb.pready) begin + cycle_end(); + cycle_start(); + end + err = apb.pslverr; + cycle_end(); + apb.paddr <= #TA '0; + apb.pwdata <= #TA '0; + apb.pstrb <= #TA '0; + apb.pwrite <= #TA 1'b0; + apb.psel <= #TA 1'b0; + apb.penable <= #TA 1'b0; + lock.put(); + endtask + + endclass + +endpackage From 4b29f2cad7503b4a5b359e0e23ef81c384cb035c Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:55:59 +0200 Subject: [PATCH 13/37] Changelog: Add missing newline at EOF --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d9827b..7fd5e57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,4 +11,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Open source release. ### Added -- Initial commit. \ No newline at end of file +- Initial commit. From be0e6b29d79a3780e7c6cae46026f3547b4443e0 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 17:58:56 +0200 Subject: [PATCH 14/37] Update Changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd5e57..1f0ba4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased +### Added +- Add clocked `APB_DV` interface for design verification. +- Define macros for assigning APB interfaces. +- Add read-only and read-write registers with APB interface. +- Add basic test infrastructure for APB modules. + +### Changed +- Rename `APB_BUS` interface to `APB`, change its parameters to constants, and remove `in` and `out` + modports. + ## 0.1.0 - 2018-09-12 ### Changed - Open source release. From efe7c5627ff5892b853da6798bb96ddc4e1a62fb Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 18:06:27 +0200 Subject: [PATCH 15/37] Update ReadMe --- README.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e80224..777857d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,34 @@ # APB -This repository contains common functions and hardware modules for the Advanced Peripherals Bus (APB). \ No newline at end of file +This is the implementation of the AMBA APB protocol, version 2, developed as part of the PULP +platform at ETH Zurich. + +Maintainer: Andreas Kurth + +## Overview + +### Interfaces + +| Name | Description | +|---------------------------|-------------------------------------------------------------------| +| `APB` | APB2 interface with 32-bit address and data channels | +| `APB_DV` | Clocked variant of `APB` for design verification | + +### Leaf Modules + +| Name | Description | +|---------------------------|-------------------------------------------------------------------| +| `apb_ro_regs` | Registers with read-only APB interface | +| `apb_rw_regs` | Registers with read- and writable via APB | + +### Intermediary Modules + +| Name | Description | +|---------------------------|-------------------------------------------------------------------| +| `apb_bus` | APB bus with single master and multiple slave interfaces | + +### Verification and Simulation + +| Name | Description | +|---------------------------|-------------------------------------------------------------------| +| `apb_driver` | APB driver (can act as either slave or master) | From a8b9b4cb6e8558dfed7aeca75882ea2ca8ca0ee6 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 3 Jun 2019 18:20:54 +0200 Subject: [PATCH 16/37] Add EditorConfig --- .editorconfig | 22 ++++++++++++++++++++++ .gitignore | 1 + 2 files changed, 23 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7b1315a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig (http://editorconfig.org/) +root = true + +# Default Settings +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +tab_width = 4 +trim_trailing_whitespace = true +max_line_length = 100 + +[Makefile] +indent_style = tab + +[{*.sv,*.svh}] +indent_size = 2 + +[*.yml] +indent_size = 2 diff --git a/.gitignore b/.gitignore index ff59ba3..4ff97c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .* !.git* +!/.editorconfig /build /Bender.lock /Bender.local From c4e558c1a9f4782ad26bad9ad6791075901d246b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:15:49 +0100 Subject: [PATCH 17/37] apb_typedef: Add macros to define APB structs --- include/apb/typedef.svh | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 include/apb/typedef.svh diff --git a/include/apb/typedef.svh b/include/apb/typedef.svh new file mode 100644 index 0000000..462cf3c --- /dev/null +++ b/include/apb/typedef.svh @@ -0,0 +1,44 @@ +// Copyright (c) 2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: +// Wolfgang Roenninger + +// Macros to define APB4 Request/Response Structs + +`ifndef APB_TYPEDEF_SVH_ +`define APB_TYPEDEF_SVH_ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// APB4 (v2.0) Request/Response Structs +// +// Usage Example: +// `APB_TYPEDEF_REQ_T ( apb_req_t, addr_t, data_t, strb_t ) +// `APB_TYPEDEF_RESP_T ( apb_resp_t, data_t ) +`define APB_TYPEDEF_REQ_T(apb_req_t, addr_t, data_t, strb_t) \ + typedef struct packed { \ + addr_t paddr; \ + apb_pkg::prot_t pprot; \ + logic psel; \ + logic penable; \ + logic pwrite; \ + data_t pwdata; \ + strb_t pstrb; \ + } apb_req_t; +`define APB_TYPEDEF_RESP_T(apb_resp_t, data_t) \ + typedef struct packed { \ + logic pready; \ + data_t prdata; \ + logic pslverr; \ + } apb_resp_t; +//////////////////////////////////////////////////////////////////////////////////////////////////// + +`endif From 9e76c4ede01a9f145958bbe5848e4096eca5d064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:16:21 +0100 Subject: [PATCH 18/37] apb_assign: Add macros to assign APB structs --- include/apb/assign.svh | 155 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 143 insertions(+), 12 deletions(-) diff --git a/include/apb/assign.svh b/include/apb/assign.svh index d73c37e..ce8fc72 100644 --- a/include/apb/assign.svh +++ b/include/apb/assign.svh @@ -12,17 +12,148 @@ `ifndef APB_ASSIGN_SVH_ `define APB_ASSIGN_SVH_ -// Assign an APB2 master interface to a slave interface, as in `assign slv = mst;`. -`define APB_ASSIGN(slv, mst) \ - assign slv.paddr = mst.paddr; \ - assign slv.pprot = mst.pprot; \ - assign slv.psel = mst.psel; \ - assign slv.penable = mst.penable; \ - assign slv.pwrite = mst.pwrite; \ - assign slv.pwdata = mst.pwdata; \ - assign slv.pstrb = mst.pstrb; \ - assign mst.pready = slv.pready; \ - assign mst.prdata = slv.prdata; \ - assign mst.pslverr = slv.pslverr; +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Assign an APB4 interface to another, as if you would do in `assign slv = mst;`. +// +// Usage example: +// `APB_ASSIGN(slv, mst) +`define APB_ASSIGN(dst, src) \ + assign dst.paddr = src.paddr; \ + assign dst.pprot = src.pprot; \ + assign dst.psel = src.psel; \ + assign dst.penable = src.penable; \ + assign dst.pwrite = src.pwrite; \ + assign dst.pwdata = src.pwdata; \ + assign dst.pstrb = src.pstrb; \ + assign src.pready = dst.pready; \ + assign src.prdata = dst.prdata; \ + assign src.pslverr = dst.pslverr; +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Internal implementation for assigning interfaces from structs, allows for standalone assignments +// (with `opt_as = assign`) and assignments inside process (with `opt_as` void) with the same code. +`define APB_FROM_REQ(opt_as, apb_if, req_struct) \ + opt_as apb_if.paddr = req_struct.paddr; \ + opt_as apb_if.pprot = req_struct.pprot; \ + opt_as apb_if.psel = req_struct.psel; \ + opt_as apb_if.penable = req_struct.penable; \ + opt_as apb_if.pwrite = req_struct.pwrite; \ + opt_as apb_if.pwdata = req_struct.pwdata; \ + opt_as apb_if.pstrb = req_struct.pstrb; +`define APB_FROM_RESP(opt_as, apb_if, resp_struct) \ + opt_as apb_if.pready = resp_struct.pready; \ + opt_as apb_if.prdata = resp_struct.prdata; \ + opt_as apb_if.pslverr = resp_struct.pslverr; +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Setting an interface from request/response structs inside a process. +// +// Usage Example: +// always_comb begin +// `APB_SET_FROM_REQ(my_if, my_req_struct) +// `APB_SET_FROM_RESP(my_if, my_resp_struct) +// end +`define APB_SET_FROM_REQ ( apb_if, req_struct ) `APB_FROM_REQ (, apb_if, req_struct ) +`define APB_SET_FROM_RESP ( apb_if, resp_struct ) `APB_FROM_RESP (, apb_if, resp_struct ) +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Assigning an interface from request/response structs outside a process. +// +// Usage Example: +// `AXI_ASSIGN_FROM_REQ(my_if, my_req_struct) +// `AXI_ASSIGN_FROM_RESP(my_if, my_resp_struct) +`define APB_ASSIGN_FROM_REQ ( apb_if, req_struct ) `APB_FROM_REQ ( assign, apb_if, req_struct ) +`define APB_ASSIGN_FROM_RESP ( apb_if, resp_struct ) `APB_FROM_RESP ( assign, apb_if, resp_struct ) +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Internal implementation for assigning to structs from interfaces, allows for standalone +// assignments (with `opt_as = assign`) and assignments inside processes (with `opt_as` void) with +// the same code. +`define APB_TO_REQ(opt_as, req_struct, apb_if) \ + opt_as req_struct = '{ \ + paddr: apb_if.paddr, \ + pprot: apb_if.pprot, \ + psel: apb_if.psel, \ + penable: apb_if.penable, \ + pwrite: apb_if.pwrite, \ + pwdata: apb_if.pwdata, \ + pstrb: apb_if.pstrb \ + }; +`define APB_TO_RESP(opt_as, resp_struct, apb_if) \ + opt_as req_struct = '{ \ + pready: apb_if.pready, \ + prdata: apb_if.prdata, \ + pslverr: apb_if.pslverr \ + }; +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Setting to an interface request/response structs inside a process. +// +// Usage Example: +// always_comb begin +// `APB_SET_TO_REQ(my_req_struct, my_if); +// `APB_SET_TO_RESP(my_resp_struct, my_if); +// end +`define APB_SET_TO_REQ ( req_struct, apb_if ) `APB_TO_REQ (, req_struct, apb_if ) +`define APB_SET_TO_RESP ( resp_struct, apb_if ) `APB_TO_RESP (, resp_struct, apb_if ) +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Assigning to an interface request/response structs outside a process. +// +// Usage Example: +// `APB_ASSIGN_TO_REQ(my_req_struct, my_if); +`define APB_ASSIGN_TO_REQ ( req_struct, apb_if) `APB_TO_REQ ( assign, req_struct, apb_if ) +`define APB_ASSIGN_TO_RESP ( resp_struct, apb_if) `APB_TO_RESP ( assign, resp_struct, apb_if ) +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Internal implementation for selecting a sel_t from structs to structs, allows for standalone +// assignments (with `opt_as = assign`) and assignments inside process (with `opt_as` void) with +// the same code. +// requires that `slv_req_struct` has a `psel` width of `1'b1` and `mst_req_struct` has a `psel` +// width < `psel_idx` +`define APB_SEL_IDX ( opt_as, slv_req_struct, mst_req_struct, psel_idx ) \ + opt_as slv_req_struct = '{ \ + paddr: mst_req_struct.paddr, \ + pprot: mst_req_struct.pprot, \ + psel: mst_req_struct.psel[psel_idx], \ + penable: mst_req_struct.penable, \ + pwrite: mst_req_struct.pwrite, \ + pwdata: mst_req_struct.pwdata, \ + pstrb: mst_req_struct.pstrb \ + }; +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Setting a selection from one to another inside a process. +// `my_psel_idx` is the index of the `psel` signal in `my_mst_req_struct`. +// The `psel` signal in `my_slv_req_struct` has to have a width of 1. +// +// Usage Example: +// always_comb begin +// `APB_SET_SEL ( my_slv_req_struct, my_mst_req_struct, my_psel_idx ) +// end +`define APB_SET_SEL ( slv_req_struct, my_mst_req_struct, psel_idx ) \ + `APB_SEL_IDX (, slv_req_struct, mst_req_struct, psel_idx ) +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Setting a selection from one to another inside a process. +// `my_psel_idx` is the index of the `psel` signal in `my_mst_req_struct`. +// The `psel` signal in `my_slv_req_struct` has to have a width of 1. +// +// Usage Example: +// always_comb begin +// `APB_ASSIGN_SEL ( my_slv_req_struct, my_mst_req_struct, my_psel_idx ) +// end +`define APB_ASSIGN_SEL ( slv_req_struct, my_mst_req_struct, psel_idx ) \ + `APB_SEL_IDX ( assign, slv_req_struct, mst_req_struct, psel_idx ) +//////////////////////////////////////////////////////////////////////////////////////////////////// `endif From 2263f8826be983b6e3976d4d6695682f53fc0777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:17:44 +0100 Subject: [PATCH 19/37] apb_intf: Parameterize interface with variable `paddr`, `pdata`, and `psel` --- src/apb_intf.sv | 84 ++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index 0f14b76..ad60544 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -9,28 +9,28 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -// An APB2 interface -interface APB; - - localparam ADDR_WIDTH = 32; - localparam DATA_WIDTH = 32; - localparam STRB_WIDTH = DATA_WIDTH / 8; - - typedef logic [ADDR_WIDTH-1:0] addr_t; - typedef logic [DATA_WIDTH-1:0] data_t; - typedef logic [2:0] prot_t; - typedef logic [STRB_WIDTH-1:0] strb_t; +// An APB4 (v2.0) interface +interface APB ( + parameter int unsigned AddrWidth = 32'd32, + parameter int unsigned DataWidth = 32'd32, + parameter int unsigned PselWidth = 32'd1 +); + localparam int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8); + typedef logic [AddrWidth-1:0] addr_t; + typedef logic [PselWidth-1:0] psel_t; + typedef logic [DataWidth-1:0] data_t; + typedef logic [StrbWidth-1:0] strb_t; - addr_t paddr; - prot_t pprot; - logic psel; - logic penable; - logic pwrite; - data_t pwdata; - strb_t pstrb; - logic pready; - data_t prdata; - logic pslverr; + addr_t paddr; + apb_pkg::prot_t pprot; + psel_t psel; + logic penable; + logic pwrite; + data_t pwdata; + strb_t pstrb; + logic pready; + data_t prdata; + logic pslverr; modport Master ( output paddr, pprot, psel, penable, pwrite, pwdata, pstrb, @@ -44,30 +44,30 @@ interface APB; endinterface -// A clocked APB2 interface for use in design verification -interface APB_DV ( +// A clocked APB4 (v2.0) interface for use in design verification +interface APB_DV #( + parameter int unsigned AddrWidth = 32'd32, + parameter int unsigned DataWidth = 32'd32, + parameter int unsigned PselWidth = 32'd1 +) ( input logic clk_i ); + localparam int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8); + typedef logic [AddrWidth-1:0] addr_t; + typedef logic [PselWidth-1:0] psel_t; + typedef logic [DataWidth-1:0] data_t; + typedef logic [StrbWidth-1:0] strb_t; - localparam ADDR_WIDTH = 32; - localparam DATA_WIDTH = 32; - localparam STRB_WIDTH = DATA_WIDTH / 8; - - typedef logic [ADDR_WIDTH-1:0] addr_t; - typedef logic [DATA_WIDTH-1:0] data_t; - typedef logic [2:0] prot_t; - typedef logic [STRB_WIDTH-1:0] strb_t; - - addr_t paddr; - prot_t pprot; - logic psel; - logic penable; - logic pwrite; - data_t pwdata; - strb_t pstrb; - logic pready; - data_t prdata; - logic pslverr; + apb_pkg::addr_t paddr; + apb_pkg::prot_t pprot; + psel_t psel; + logic penable; + logic pwrite; + apb_pkg::data_t pwdata; + apb_pkg::strb_t pstrb; + logic pready; + apb_pkg::data_t prdata; + logic pslverr; modport Master ( output paddr, pprot, psel, penable, pwrite, pwdata, pstrb, From 2fd243c6524288ec1522f80fb28d9f4c0538ba0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:19:15 +0100 Subject: [PATCH 20/37] apb_test: Add `psel` index for read and write tasks --- src/apb_test.sv | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/apb_test.sv b/src/apb_test.sv index 7feaaec..cc16b59 100644 --- a/src/apb_test.sv +++ b/src/apb_test.sv @@ -29,7 +29,7 @@ package apb_test; function void reset_master(); apb.paddr <= '0; apb.pprot <= '0; - apb.psel <= 1'b0; + apb.psel <= '0; apb.penable <= 1'b0; apb.pwrite <= 1'b0; apb.pwdata <= '0; @@ -52,17 +52,18 @@ package apb_test; task read( input logic [31:0] addr, + input int unsigned psel_idx, output logic [31:0] data, - output logic err + output logic err ); while (!lock.try_get()) begin cycle_end(); end - apb.paddr <= #TA addr; - apb.pwrite <= #TA 1'b0; - apb.psel <= #TA 1'b1; + apb.paddr <= #TA addr; + apb.pwrite <= #TA 1'b0; + apb.psel[psel_idx] <= #TA 1'b1; cycle_end(); - apb.penable <= #TA 1'b1; + apb.penable <= #TA 1'b1; cycle_start(); while (!apb.pready) begin cycle_end(); @@ -72,13 +73,14 @@ package apb_test; err = apb.pslverr; cycle_end(); apb.paddr <= #TA '0; - apb.psel <= #TA 1'b0; + apb.psel <= #TA '0; apb.penable <= #TA 1'b0; lock.put(); endtask task write( input logic [31:0] addr, + input int unsigned psel_idx, input logic [31:0] data, input logic [3:0] strb, output logic err @@ -86,13 +88,13 @@ package apb_test; while (!lock.try_get()) begin cycle_end(); end - apb.paddr <= #TA addr; - apb.pwdata <= #TA data; - apb.pstrb <= #TA strb; - apb.pwrite <= #TA 1'b1; - apb.psel <= #TA 1'b1; + apb.paddr <= #TA addr; + apb.pwdata <= #TA data; + apb.pstrb <= #TA strb; + apb.pwrite <= #TA 1'b1; + apb.psel[psel_idx] <= #TA 1'b1; cycle_end(); - apb.penable <= #TA 1'b1; + apb.penable <= #TA 1'b1; cycle_start(); while (!apb.pready) begin cycle_end(); @@ -104,7 +106,7 @@ package apb_test; apb.pwdata <= #TA '0; apb.pstrb <= #TA '0; apb.pwrite <= #TA 1'b0; - apb.psel <= #TA 1'b0; + apb.psel <= #TA '0; apb.penable <= #TA 1'b0; lock.put(); endtask From 39cf51143f941c6ef56394d32aa509cc6dd47066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:19:59 +0100 Subject: [PATCH 21/37] apb_ro_regs: Add variable address and data width --- src/apb_ro_regs.sv | 55 +++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/apb_ro_regs.sv b/src/apb_ro_regs.sv index ed2d4ef..3d1a233 100644 --- a/src/apb_ro_regs.sv +++ b/src/apb_ro_regs.sv @@ -9,41 +9,50 @@ // specific language governing permissions and limitations under the License. // APB Read-Only Registers -// This module exposes a number of 32-bit registers (provided on the `reg_i` input) read-only on an +// This module exposes a number of registers (provided on the `reg_i` input) read-only on an // APB interface. It responds to reads that are out of range and writes with a slave error. module apb_ro_regs #( - parameter int unsigned N_REGS = 0 + parameter int unsigned NoRegs = 32'd0, + parameter int unsigned AddrWidth = 32'd0, + parameter int unsigned DataWidth = 32'd0, + // DEPENDENT PARAMETERS DO NOT OVERWRITE! + parameter int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8), + parameter type addr_t = logic[AddrWidth-1:0], + parameter type data_t = logic[DataWidth-1:0], + parameter type strb_t = logic[StrbWidth-1:0] ) ( // APB Interface - input logic pclk_i, - input logic preset_ni, - input logic [31:0] paddr_i, - input logic [2:0] pprot_i, - input logic psel_i, - input logic penable_i, - input logic pwrite_i, - input logic [31:0] pwdata_i, - input logic [3:0] pstrb_i, - output logic pready_o, - output logic [31:0] prdata_o, - output logic pslverr_o, + input logic pclk_i, + input logic preset_ni, + input addr_t paddr_i, + input apb_pkg::prot_t pprot_i, + input logic psel_i, + input logic penable_i, + input logic pwrite_i, + input data_t pwdata_i, + input strb_t pstrb_i, + output logic pready_o, + output data_t prdata_o, + output logic pslverr_o, // Register Interface - input logic [N_REGS-1:0][31:0] reg_i + input data_t [NoRegs-1:0] reg_i ); + // if StrbWidth = 1 we want Word Offset = 0 + localparam int unsigned WordOffset = $clog2(StrbWidth); always_comb begin - prdata_o = 'x; - pslverr_o = 1'b0; + prdata_o = data_t'(32'h0BAD_B10C); + pslverr_o = apb_pkg::RESP_OKAY; if (psel_i) begin if (pwrite_i) begin // Error response to writes - pslverr_o = 1'b1; + pslverr_o = apb_pkg::RESP_SLVERR; end else begin - automatic logic [29:0] word_addr = paddr_i >> 2; - if (word_addr >= N_REGS) begin + automatic logic [AddrWidth-WordOffset-1:0] word_addr = paddr_i >> WordOffset; + if (word_addr >= NoRegs) begin // Error response to reads out of range - pslverr_o = 1'b1; + pslverr_o = apb_pkg::RESP_SLVERR; end else begin prdata_o = reg_i[word_addr]; end @@ -56,7 +65,9 @@ module apb_ro_regs #( // pragma translate_off `ifndef VERILATOR initial begin: p_assertions - assert (N_REGS >= 1) else $fatal(1, "The number of registers must be at least 1!"); + assert (NoRegs > 0) else $fatal(1, "The number of registers must be at least 1!"); + assert (AddrWidth > WordOffset) else $fatal(1, "AddrWidth is not wide enough!"); + assert (DataWidth > 0) else $fatal(1, "DataWidth has to be > 0!"); end `endif // pragma translate_on From c94d1f92e3d40052dcf9e563ee2e23bea5ecc67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:21:13 +0100 Subject: [PATCH 22/37] apb_rw_regs: Add variable address and data width --- src/apb_rw_regs.sv | 78 ++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/apb_rw_regs.sv b/src/apb_rw_regs.sv index eb289d0..05780b1 100644 --- a/src/apb_rw_regs.sv +++ b/src/apb_rw_regs.sv @@ -9,48 +9,62 @@ // specific language governing permissions and limitations under the License. // APB Read-Write Registers -// This module exposes a number of 32-bit registers (provided on the `reg_i` input) writable on an +// This module exposes a number of registers (provided on the `reg_i` input) writable on an // APB interface. It responds to accesses that are out of range with a slave error. +`include "common_cells/registers.svh" + module apb_rw_regs #( - parameter int unsigned N_REGS = 0 + parameter int unsigned NoRegs = 0 + parameter int unsigned AddrWidth = 32'd0, + parameter int unsigned DataWidth = 32'd0, + // DEPENDENT PARAMETERS DO NOT OVERWRITE! + parameter int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8), + parameter type addr_t = logic[AddrWidth-1:0], + parameter type data_t = logic[DataWidth-1:0], + parameter type strb_t = logic[StrbWidth-1:0] ) ( // APB Interface - input logic pclk_i, - input logic preset_ni, - input logic [31:0] paddr_i, - input logic [2:0] pprot_i, - input logic psel_i, - input logic penable_i, - input logic pwrite_i, - input logic [31:0] pwdata_i, - input logic [3:0] pstrb_i, - output logic pready_o, - output logic [31:0] prdata_o, - output logic pslverr_o, + input logic pclk_i, + input logic preset_ni, + input addr_t paddr_i, + input apb_pkg::prot_t pprot_i, + input logic psel_i, + input logic penable_i, + input logic pwrite_i, + input data_t pwdata_i, + input strb_t pstrb_i, + output logic pready_o, + output data_t prdata_o, + output logic pslverr_o, // Register Interface - input logic [N_REGS-1:0][31:0] init_i, - output logic [N_REGS-1:0][31:0] q_o + input data_t [NoRegs-1:0] init_i, + output data_t [NoRegs-1:0] q_o ); + // if StrbWidth = 1 we want Word Offset = 0 + localparam int unsigned WordOffset = $clog2(StrbWidth); - logic [N_REGS-1:0][31:0] reg_d, reg_q; + data_t [NoRegs-1:0] reg_d, reg_q; + logic reg_update; always_comb begin - reg_d = reg_q; - prdata_o = 'x; - pslverr_o = 1'b0; + reg_d = reg_q; + reg_update = 1'b0; + prdata_o = data_t'(32'h0BAD_B10C); + pslverr_o = apb_pkg::RESP_OKAY; if (psel_i) begin - automatic logic [29:0] word_addr = paddr_i >> 2; - if (word_addr >= N_REGS) begin + automatic logic [AddrWidth-WordOffset-1:0] word_addr = paddr_i >> WordOffset; + if (word_addr >= NoRegs) begin // Error response to accesses that are out of range - pslverr_o = 1'b1; + pslverr_o = apb_pkg::RESP_SLVERR; end else begin if (pwrite_i) begin - for (int i = 0; i < 4; i++) begin - if (pstrb_i[i]) begin - reg_d[word_addr][i*8 +: 8] = pwdata_i[i*8 +: 8]; + for (int unsigned i = 0; i < DataWidth; i++) begin + if (pstrb_i[i/8]) begin + reg_d[word_addr][i] = pwdata_i[i]; end end + reg_update = |pstrb_i; // only update register when a write strobe is set end else begin prdata_o = reg_q[word_addr]; end @@ -61,19 +75,15 @@ module apb_rw_regs #( assign q_o = reg_q; - always_ff @(posedge pclk_i or negedge preset_ni) begin - if (!preset_ni) begin - reg_q <= init_i; - end else begin - reg_q <= reg_d; - end - end + `FFLARN(reg_q, reg_d, reg_update, init_i, pclk_i, preset_ni) // Validate parameters. // pragma translate_off `ifndef VERILATOR initial begin: p_assertions - assert (N_REGS >= 1) else $fatal(1, "The number of registers must be at least 1!"); + assert (NoRegs > 0) else $fatal(1, "The number of registers must be at least 1!"); + assert (AddrWidth > WordOffset) else $fatal(1, "AddrWidth is not wide enough!"); + assert (DataWidth > 0) else $fatal(1, "DataWidth has to be > 0!"); end `endif // pragma translate_on From fb795d08982468a11eff3c73280d82807a2569eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:21:55 +0100 Subject: [PATCH 23/37] apb_pkg: Add package with `pprot_t` and error definitions --- src/apb_pkg.sv | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/apb_pkg.sv diff --git a/src/apb_pkg.sv b/src/apb_pkg.sv new file mode 100644 index 0000000..fd53fb9 --- /dev/null +++ b/src/apb_pkg.sv @@ -0,0 +1,22 @@ +// Copyright (c) 2019 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Wolfgang Roenninger + +// Description: Package with constant APB v2.0 constants + +package apb_pkg; + + typedef logic [2:0] prot_t; + + localparam RESP_OKAY = 1'b0; + localparam RESP_SLVERR = 1'b1; + +endpackage From e2e80193433c765749b1308ed034970d68e22a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:22:23 +0100 Subject: [PATCH 24/37] Bender: Update with new files --- Bender.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Bender.yml b/Bender.yml index efcd84a..131dc70 100644 --- a/Bender.yml +++ b/Bender.yml @@ -3,9 +3,10 @@ package: authors: - "Andreas Kurth " # current maintainer - "Fabian Schuiki " + - "Wolfgang Roenninger " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.10.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.16.2 } export_include_dirs: - include @@ -15,8 +16,10 @@ sources: # package. Files in level 1 only depend on files in level 0, files in level 2 on files in # levels 1 and 0, etc. Files within a level are ordered alphabetically. # Level 0 - - src/apb_intf.sv + - src/apb_pkg.sv # Level 1 + - src/apb_intf.sv + # Level 2 - src/apb_bus.sv - src/apb_ro_regs.sv - src/apb_rw_regs.sv From 8fabeded3489818f8bbbc888d37b1c2853ec217f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 10 Feb 2020 17:39:57 +0100 Subject: [PATCH 25/37] README: Update with new modules --- README.md | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 777857d..35ccc10 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,43 @@ # APB -This is the implementation of the AMBA APB protocol, version 2, developed as part of the PULP +This is the implementation of the AMBA APB4 protocol, version 2.0, developed as part of the PULP platform at ETH Zurich. Maintainer: Andreas Kurth ## Overview +### Package / Macros + +| Name | Description | +|------------------------------------------|-------------------------------------------------------------------| +| [`apb_pkg`](src/apb_pkg.sv) | Package with APB4 constants and type definitions | +| [`apb/typedef`](include/apb/typedef.svh) | Macros which define the APB4 request/response structs | +| [`apb/assign`](include/apb/typedef.svh) | Macros which assign/set/translates APB4 interfaces and structs | + + ### Interfaces -| Name | Description | -|---------------------------|-------------------------------------------------------------------| -| `APB` | APB2 interface with 32-bit address and data channels | -| `APB_DV` | Clocked variant of `APB` for design verification | +| Name | Description | +|------------------------------------------|-------------------------------------------------------------------| +| [`APB`](src/apb_intf.sv) | APB4 interface with configurable address, data and sel widths | +| [`APB_DV`](src/apb_intf.sv) | Clocked variant of `APB` for design verification | ### Leaf Modules -| Name | Description | -|---------------------------|-------------------------------------------------------------------| -| `apb_ro_regs` | Registers with read-only APB interface | -| `apb_rw_regs` | Registers with read- and writable via APB | +| Name | Description | +|------------------------------------------|-------------------------------------------------------------------| +| [`apb_ro_regs`](src/apb_ro_regs.sv) | Read-only registers | +| [`apb_rw_regs`](src/apb_rw_regs.sv) | Read and write registers | ### Intermediary Modules -| Name | Description | -|---------------------------|-------------------------------------------------------------------| -| `apb_bus` | APB bus with single master and multiple slave interfaces | +| Name | Description | +|------------------------------------------|-------------------------------------------------------------------| +| [`apb_bus`](src/apb_bus.sv) | APB bus with single master and multiple slave interfaces | ### Verification and Simulation -| Name | Description | -|---------------------------|-------------------------------------------------------------------| -| `apb_driver` | APB driver (can act as either slave or master) | +| Name | Description | +|------------------------------------------|-------------------------------------------------------------------| +| [`apb_driver`](src/apb_test.sv) | APB driver (can act as either slave or master) | From 237b2caa8f5adaa91646889f8a040898ddea1f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Thu, 20 Feb 2020 11:21:14 +0100 Subject: [PATCH 26/37] apb_intf: Remove parametrizable `psel` --- src/apb_intf.sv | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index ad60544..efaf1e1 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2014 ETH Zurich, University of Bologna +// Copyright (c) 2014-2020 ETH Zurich, University of Bologna // // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in @@ -12,18 +12,16 @@ // An APB4 (v2.0) interface interface APB ( parameter int unsigned AddrWidth = 32'd32, - parameter int unsigned DataWidth = 32'd32, - parameter int unsigned PselWidth = 32'd1 + parameter int unsigned DataWidth = 32'd32 ); localparam int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8); typedef logic [AddrWidth-1:0] addr_t; - typedef logic [PselWidth-1:0] psel_t; typedef logic [DataWidth-1:0] data_t; typedef logic [StrbWidth-1:0] strb_t; addr_t paddr; apb_pkg::prot_t pprot; - psel_t psel; + logic psel; logic penable; logic pwrite; data_t pwdata; @@ -47,20 +45,18 @@ endinterface // A clocked APB4 (v2.0) interface for use in design verification interface APB_DV #( parameter int unsigned AddrWidth = 32'd32, - parameter int unsigned DataWidth = 32'd32, - parameter int unsigned PselWidth = 32'd1 + parameter int unsigned DataWidth = 32'd32 ) ( input logic clk_i ); localparam int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8); typedef logic [AddrWidth-1:0] addr_t; - typedef logic [PselWidth-1:0] psel_t; typedef logic [DataWidth-1:0] data_t; typedef logic [StrbWidth-1:0] strb_t; apb_pkg::addr_t paddr; apb_pkg::prot_t pprot; - psel_t psel; + logic psel; logic penable; logic pwrite; apb_pkg::data_t pwdata; From acce8ec8ef3289855c20d840e47659b015e1a73b Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Fri, 13 Mar 2020 10:52:13 +0100 Subject: [PATCH 27/37] apb_intf: Fix capitalization of parameters --- src/apb_intf.sv | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/apb_intf.sv b/src/apb_intf.sv index efaf1e1..9e9341b 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -11,13 +11,13 @@ // An APB4 (v2.0) interface interface APB ( - parameter int unsigned AddrWidth = 32'd32, - parameter int unsigned DataWidth = 32'd32 + parameter int unsigned ADDR_WIDTH = 32'd32, + parameter int unsigned DATA_WIDTH = 32'd32 ); - localparam int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8); - typedef logic [AddrWidth-1:0] addr_t; - typedef logic [DataWidth-1:0] data_t; - typedef logic [StrbWidth-1:0] strb_t; + localparam int unsigned STRB_WIDTH = cf_math_pkg::ceil_div(DATA_WIDTH, 8); + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; addr_t paddr; apb_pkg::prot_t pprot; @@ -44,15 +44,15 @@ endinterface // A clocked APB4 (v2.0) interface for use in design verification interface APB_DV #( - parameter int unsigned AddrWidth = 32'd32, - parameter int unsigned DataWidth = 32'd32 + parameter int unsigned ADDR_WIDTH = 32'd32, + parameter int unsigned DATA_WIDTH = 32'd32 ) ( input logic clk_i ); - localparam int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8); - typedef logic [AddrWidth-1:0] addr_t; - typedef logic [DataWidth-1:0] data_t; - typedef logic [StrbWidth-1:0] strb_t; + localparam int unsigned STRB_WIDTH = cf_math_pkg::ceil_div(DATA_WIDTH, 8); + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; apb_pkg::addr_t paddr; apb_pkg::prot_t pprot; From 936229b4f16cb1b42e1f06bfa3453d8c06fc7f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 13 Mar 2020 10:53:03 +0100 Subject: [PATCH 28/37] apb_test: Various improvements --- src/apb_test.sv | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/apb_test.sv b/src/apb_test.sv index cc16b59..0369ba4 100644 --- a/src/apb_test.sv +++ b/src/apb_test.sv @@ -12,12 +12,17 @@ // Test infrastructure for APB interfaces package apb_test; - // Simple APB driver with thread-safe 32-bit read and write functions + // Simple APB driver with thread-safe read and write functions class apb_driver #( - parameter time TA = 0ns, - parameter time TT = 0ns + parameter int unsigned ADDR_WIDTH = 32'd32, // APB4 address width + parameter int unsigned DATA_WIDTH = 32'd32, // APB4 data width + parameter time TA = 0ns, // application time + parameter time TT = 0ns // test time ); - + localparam int unsigned STRB_WIDTH = cf_math_pkg::ceil_div(DATA_WIDTH, 8); + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; virtual APB_DV apb; semaphore lock; @@ -29,7 +34,7 @@ package apb_test; function void reset_master(); apb.paddr <= '0; apb.pprot <= '0; - apb.psel <= '0; + apb.psel <= 1'b0; apb.penable <= 1'b0; apb.pwrite <= 1'b0; apb.pwdata <= '0; @@ -50,20 +55,20 @@ package apb_test; @(posedge apb.clk_i); endtask + // this task reads from an APB4 slave, acts as master task read( - input logic [31:0] addr, - input int unsigned psel_idx, - output logic [31:0] data, - output logic err + input addr_t addr, + output data_t data, + output logic err ); while (!lock.try_get()) begin cycle_end(); end - apb.paddr <= #TA addr; - apb.pwrite <= #TA 1'b0; - apb.psel[psel_idx] <= #TA 1'b1; + apb.paddr <= #TA addr; + apb.pwrite <= #TA 1'b0; + apb.psel <= #TA 1'b1; cycle_end(); - apb.penable <= #TA 1'b1; + apb.penable <= #TA 1'b1; cycle_start(); while (!apb.pready) begin cycle_end(); @@ -73,17 +78,17 @@ package apb_test; err = apb.pslverr; cycle_end(); apb.paddr <= #TA '0; - apb.psel <= #TA '0; + apb.psel <= #TA 1'b0; apb.penable <= #TA 1'b0; lock.put(); endtask + // this task writes to an APB4 slave, acts as master task write( - input logic [31:0] addr, - input int unsigned psel_idx, - input logic [31:0] data, - input logic [3:0] strb, - output logic err + input addr_t addr, + input data_t data, + input strb_t strb, + output logic err ); while (!lock.try_get()) begin cycle_end(); From 54cd483dae31070a6a233361b8cf5160a2074b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 13 Mar 2020 10:58:51 +0100 Subject: [PATCH 29/37] apb_ro_regs: Rework and add testbench --- Bender.yml | 4 + CHANGELOG.md | 1 + include/apb/assign.svh | 64 +++----------- src/apb_intf.sv | 10 +-- src/apb_ro_regs.sv | 190 +++++++++++++++++++++++++++++++---------- src/apb_rw_regs.sv | 2 +- src/apb_test.sv | 21 +++-- test/tb_apb_ro_regs.sv | 121 ++++++++++++++++++++++++++ 8 files changed, 297 insertions(+), 116 deletions(-) create mode 100644 test/tb_apb_ro_regs.sv diff --git a/Bender.yml b/Bender.yml index 131dc70..502bb56 100644 --- a/Bender.yml +++ b/Bender.yml @@ -27,3 +27,7 @@ sources: - target: simulation files: - src/apb_test.sv + + - target: test + files: + - test/tb_apb_ro_regs.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0ba4a..e398579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Rename `APB_BUS` interface to `APB`, change its parameters to constants, and remove `in` and `out` modports. +- `apb_ro_regs`: Use of `addr_decode` module for indexing and change to structs. ## 0.1.0 - 2018-09-12 ### Changed diff --git a/include/apb/assign.svh b/include/apb/assign.svh index ce8fc72..f13fd67 100644 --- a/include/apb/assign.svh +++ b/include/apb/assign.svh @@ -55,18 +55,18 @@ // `APB_SET_FROM_REQ(my_if, my_req_struct) // `APB_SET_FROM_RESP(my_if, my_resp_struct) // end -`define APB_SET_FROM_REQ ( apb_if, req_struct ) `APB_FROM_REQ (, apb_if, req_struct ) -`define APB_SET_FROM_RESP ( apb_if, resp_struct ) `APB_FROM_RESP (, apb_if, resp_struct ) +`define APB_SET_FROM_REQ(apb_if, req_struct) `APB_FROM_REQ(, apb_if, req_struct) +`define APB_SET_FROM_RESP(apb_if, resp_struct) `APB_FROM_RESP(, apb_if, resp_struct) //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // Assigning an interface from request/response structs outside a process. // // Usage Example: -// `AXI_ASSIGN_FROM_REQ(my_if, my_req_struct) -// `AXI_ASSIGN_FROM_RESP(my_if, my_resp_struct) -`define APB_ASSIGN_FROM_REQ ( apb_if, req_struct ) `APB_FROM_REQ ( assign, apb_if, req_struct ) -`define APB_ASSIGN_FROM_RESP ( apb_if, resp_struct ) `APB_FROM_RESP ( assign, apb_if, resp_struct ) +// `APB_ASSIGN_FROM_REQ(my_if, my_req_struct) +// `APB_ASSIGN_FROM_RESP(my_if, my_resp_struct) +`define APB_ASSIGN_FROM_REQ(apb_if, req_struct) `APB_FROM_REQ(assign, apb_if, req_struct) +`define APB_ASSIGN_FROM_RESP(apb_if, resp_struct) `APB_FROM_RESP(assign, apb_if, resp_struct) //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -99,8 +99,8 @@ // `APB_SET_TO_REQ(my_req_struct, my_if); // `APB_SET_TO_RESP(my_resp_struct, my_if); // end -`define APB_SET_TO_REQ ( req_struct, apb_if ) `APB_TO_REQ (, req_struct, apb_if ) -`define APB_SET_TO_RESP ( resp_struct, apb_if ) `APB_TO_RESP (, resp_struct, apb_if ) +`define APB_SET_TO_REQ(req_struct, apb_if) `APB_TO_REQ(, req_struct, apb_if) +`define APB_SET_TO_RESP(resp_struct, apb_if) `APB_TO_RESP(, resp_struct, apb_if) //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -108,52 +108,8 @@ // // Usage Example: // `APB_ASSIGN_TO_REQ(my_req_struct, my_if); -`define APB_ASSIGN_TO_REQ ( req_struct, apb_if) `APB_TO_REQ ( assign, req_struct, apb_if ) -`define APB_ASSIGN_TO_RESP ( resp_struct, apb_if) `APB_TO_RESP ( assign, resp_struct, apb_if ) -//////////////////////////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Internal implementation for selecting a sel_t from structs to structs, allows for standalone -// assignments (with `opt_as = assign`) and assignments inside process (with `opt_as` void) with -// the same code. -// requires that `slv_req_struct` has a `psel` width of `1'b1` and `mst_req_struct` has a `psel` -// width < `psel_idx` -`define APB_SEL_IDX ( opt_as, slv_req_struct, mst_req_struct, psel_idx ) \ - opt_as slv_req_struct = '{ \ - paddr: mst_req_struct.paddr, \ - pprot: mst_req_struct.pprot, \ - psel: mst_req_struct.psel[psel_idx], \ - penable: mst_req_struct.penable, \ - pwrite: mst_req_struct.pwrite, \ - pwdata: mst_req_struct.pwdata, \ - pstrb: mst_req_struct.pstrb \ - }; -//////////////////////////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Setting a selection from one to another inside a process. -// `my_psel_idx` is the index of the `psel` signal in `my_mst_req_struct`. -// The `psel` signal in `my_slv_req_struct` has to have a width of 1. -// -// Usage Example: -// always_comb begin -// `APB_SET_SEL ( my_slv_req_struct, my_mst_req_struct, my_psel_idx ) -// end -`define APB_SET_SEL ( slv_req_struct, my_mst_req_struct, psel_idx ) \ - `APB_SEL_IDX (, slv_req_struct, mst_req_struct, psel_idx ) -//////////////////////////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Setting a selection from one to another inside a process. -// `my_psel_idx` is the index of the `psel` signal in `my_mst_req_struct`. -// The `psel` signal in `my_slv_req_struct` has to have a width of 1. -// -// Usage Example: -// always_comb begin -// `APB_ASSIGN_SEL ( my_slv_req_struct, my_mst_req_struct, my_psel_idx ) -// end -`define APB_ASSIGN_SEL ( slv_req_struct, my_mst_req_struct, psel_idx ) \ - `APB_SEL_IDX ( assign, slv_req_struct, mst_req_struct, psel_idx ) +`define APB_ASSIGN_TO_REQ(req_struct, apb_if) `APB_TO_REQ(assign, req_struct, apb_if) +`define APB_ASSIGN_TO_RESP(resp_struct, apb_if) `APB_TO_RESP(assign, resp_struct, apb_if) //////////////////////////////////////////////////////////////////////////////////////////////////// `endif diff --git a/src/apb_intf.sv b/src/apb_intf.sv index 9e9341b..4301439 100644 --- a/src/apb_intf.sv +++ b/src/apb_intf.sv @@ -10,7 +10,7 @@ // specific language governing permissions and limitations under the License. // An APB4 (v2.0) interface -interface APB ( +interface APB #( parameter int unsigned ADDR_WIDTH = 32'd32, parameter int unsigned DATA_WIDTH = 32'd32 ); @@ -54,15 +54,15 @@ interface APB_DV #( typedef logic [DATA_WIDTH-1:0] data_t; typedef logic [STRB_WIDTH-1:0] strb_t; - apb_pkg::addr_t paddr; + addr_t paddr; apb_pkg::prot_t pprot; logic psel; logic penable; logic pwrite; - apb_pkg::data_t pwdata; - apb_pkg::strb_t pstrb; + data_t pwdata; + strb_t pstrb; logic pready; - apb_pkg::data_t prdata; + data_t prdata; logic pslverr; modport Master ( diff --git a/src/apb_ro_regs.sv b/src/apb_ro_regs.sv index 3d1a233..7a37ba6 100644 --- a/src/apb_ro_regs.sv +++ b/src/apb_ro_regs.sv @@ -8,68 +8,164 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. +// Author: Wolfgang Roenninger + // APB Read-Only Registers // This module exposes a number of registers (provided on the `reg_i` input) read-only on an // APB interface. It responds to reads that are out of range and writes with a slave error. +// The registers are byte addressed and aligned to 4 byte (32bit)! If `reg_data_t` width is less +// than the data width of the APB4 port, the register read response gets extended with `'0`. module apb_ro_regs #( - parameter int unsigned NoRegs = 32'd0, - parameter int unsigned AddrWidth = 32'd0, - parameter int unsigned DataWidth = 32'd0, + parameter int unsigned NoApbRegs = 32'd0, // number of read only registers + parameter int unsigned ApbAddrWidth = 32'd0, // address width of `req_i.paddr` + parameter int unsigned RegDataWidth = 32'd0, // data width of the registers + parameter type req_t = logic, // APB4 request type + parameter type resp_t = logic, // APB4 response type // DEPENDENT PARAMETERS DO NOT OVERWRITE! - parameter int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8), - parameter type addr_t = logic[AddrWidth-1:0], - parameter type data_t = logic[DataWidth-1:0], - parameter type strb_t = logic[StrbWidth-1:0] + parameter type apb_addr_t = logic[ApbAddrWidth-1:0], + parameter type reg_data_t = logic[RegDataWidth-1:0] ) ( // APB Interface - input logic pclk_i, - input logic preset_ni, - input addr_t paddr_i, - input apb_pkg::prot_t pprot_i, - input logic psel_i, - input logic penable_i, - input logic pwrite_i, - input data_t pwdata_i, - input strb_t pstrb_i, - output logic pready_o, - output data_t prdata_o, - output logic pslverr_o, - + input logic pclk_i, + input logic preset_ni, + input req_t req_i, + output resp_t resp_o, // Register Interface - input data_t [NoRegs-1:0] reg_i + input apb_addr_t base_addr_i, // base address of the read only registers + input reg_data_t [NoApbRegs-1:0] reg_i ); - // if StrbWidth = 1 we want Word Offset = 0 - localparam int unsigned WordOffset = $clog2(StrbWidth); + localparam int unsigned IdxWidth = (NoApbRegs > 32'd1) ? $clog2(NoApbRegs) : 32'd1; + typedef logic [IdxWidth-1:0] idx_t; + typedef struct packed { + int unsigned idx; + apb_addr_t start_addr; + apb_addr_t end_addr; + } rule_t; + + // signal declarations + rule_t [NoApbRegs-1:0] addr_map; + idx_t reg_idx; + logic decode_valid; + // generate address map for the registers + for (genvar i = 0; i < NoApbRegs; i++) begin: gen_reg_addr_map + assign addr_map[i] = '{ + idx: unsigned'(i), + start_addr: base_addr_i + apb_addr_t'( i * 32'd4), + end_addr: base_addr_i + apb_addr_t'((i+32'd1) * 32'd4) + }; + end + // read control always_comb begin - prdata_o = data_t'(32'h0BAD_B10C); - pslverr_o = apb_pkg::RESP_OKAY; - if (psel_i) begin - if (pwrite_i) begin - // Error response to writes - pslverr_o = apb_pkg::RESP_SLVERR; + resp_o = '{ + pready: req_i.psel & req_i.penable, + prdata: '0, + pslverr: apb_pkg::RESP_OKAY + }; + if (req_i.psel) begin + if (req_i.pwrite || !decode_valid) begin + // Error response on writes and decode errors + resp_o.pslverr = apb_pkg::RESP_SLVERR; + resp_o.prdata = reg_data_t'(32'h0BAD_B10C); end else begin - automatic logic [AddrWidth-WordOffset-1:0] word_addr = paddr_i >> WordOffset; - if (word_addr >= NoRegs) begin - // Error response to reads out of range - pslverr_o = apb_pkg::RESP_SLVERR; - end else begin - prdata_o = reg_i[word_addr]; - end + resp_o.prdata = reg_i[reg_idx]; end end end - assign pready_o = psel_i & penable_i; -// Validate parameters. -// pragma translate_off -`ifndef VERILATOR - initial begin: p_assertions - assert (NoRegs > 0) else $fatal(1, "The number of registers must be at least 1!"); - assert (AddrWidth > WordOffset) else $fatal(1, "AddrWidth is not wide enough!"); - assert (DataWidth > 0) else $fatal(1, "DataWidth has to be > 0!"); - end -`endif -// pragma translate_on + addr_decode #( + .NoIndices ( NoApbRegs ), + .NoRules ( NoApbRegs ), + .addr_t ( apb_addr_t ), + .rule_t ( rule_t ) + ) i_addr_decode ( + .addr_i ( req_i.paddr ), + .addr_map_i ( addr_map ), + .idx_o ( reg_idx ), + .dec_valid_o ( decode_valid ), + .dec_error_o ( /*not used*/ ), + .en_default_idx_i ( '0 ), + .default_idx_i ( '0 ) + ); + + // Validate parameters. + // pragma translate_off + `ifndef VERILATOR + initial begin: p_assertions + assert (NoApbRegs > 32'd0) + else $fatal(1, "The number of registers must be at least 1!"); + assert (ApbAddrWidth > 32'd2) + else $fatal(1, "ApbAddrWidth is not wide enough, has to be at least 3 bit wide!"); + assert (RegDataWidth > 32'd0 && RegDataWidth <= 32'd32) + else $fatal(1, "RegDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); + assert (RegDataWidth <= $bits(resp_o.prdata)) + else $fatal(1, "RegDataWidth has to be: RegDataWidth <= $bits(req_i.prdata)!"); + assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) + else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); + assert ($bits(req_i.paddr) == ApbAddrWidth) + else $fatal(1, "AddrWidth does not match req_i.paddr!"); + end + `endif + // pragma translate_on +endmodule + +`include "apb/assign.svh" +`include "apb/typedef.svh" + +module apb_ro_regs_intf #( + parameter int unsigned NO_APB_REGS = 32'd0, // number of read only registers + parameter int unsigned APB_ADDR_WIDTH = 32'd0, // address width of `paddr` + parameter int unsigned APB_DATA_WIDTH = 32'd0, // data width of the registers + parameter int unsigned REG_DATA_WIDTH = 32'd0, + // DEPENDENT PARAMETERS DO NOT OVERWRITE! + parameter type apb_addr_t = logic[APB_ADDR_WIDTH-1:0], + parameter type reg_data_t = logic[REG_DATA_WIDTH-1:0] +) ( + // APB Interface + input logic pclk_i, + input logic preset_ni, + APB.Slave slv, + // Register Interface + input apb_addr_t base_addr_i, // base address of the read only registers + input reg_data_t [NO_APB_REGS-1:0] reg_i +); + localparam int unsigned APB_STRB_WIDTH = cf_math_pkg::ceil_div(APB_DATA_WIDTH, 8); + typedef logic [APB_DATA_WIDTH-1:0] apb_data_t; + typedef logic [APB_STRB_WIDTH-1:0] apb_strb_t; + `APB_TYPEDEF_REQ_T ( apb_req_t, apb_addr_t, apb_data_t, apb_strb_t ) + `APB_TYPEDEF_RESP_T ( apb_resp_t, apb_data_t ) + + apb_req_t apb_req; + apb_resp_t apb_resp; + + `APB_ASSIGN_TO_REQ ( apb_req, slv ) + `APB_ASSIGN_FROM_RESP ( slv, apb_resp ) + + apb_ro_regs #( + .NoApbRegs ( NO_APB_REGS ), + .ApbAddrWidth ( APB_ADDR_WIDTH ), + .RegDataWidth ( REG_DATA_WIDTH ), + .req_t ( apb_req_t ), + .resp_t ( apb_resp_t ) + ) i_apb_ro_regs ( + .pclk_i, + .preset_ni, + .req_i ( apb_req ), + .resp_o ( apb_resp ), + .base_addr_i, + .reg_i + ); + + // Validate parameters. + // pragma translate_off + `ifndef VERILATOR + initial begin: p_assertions + assert (APB_ADDR_WIDTH == $bits(slv.paddr)) + else $fatal(1, "APB_ADDR_WIDTH does not match slv interface!"); + assert (APB_DATA_WIDTH == $bits(slv.pwdata)) + else $fatal(1, "APB_DATA_WIDTH does not match slv interface!"); + end + `endif + // pragma translate_on endmodule diff --git a/src/apb_rw_regs.sv b/src/apb_rw_regs.sv index 05780b1..41a737d 100644 --- a/src/apb_rw_regs.sv +++ b/src/apb_rw_regs.sv @@ -14,7 +14,7 @@ `include "common_cells/registers.svh" module apb_rw_regs #( - parameter int unsigned NoRegs = 0 + parameter int unsigned NoRegs = 0, parameter int unsigned AddrWidth = 32'd0, parameter int unsigned DataWidth = 32'd0, // DEPENDENT PARAMETERS DO NOT OVERWRITE! diff --git a/src/apb_test.sv b/src/apb_test.sv index 0369ba4..94844fd 100644 --- a/src/apb_test.sv +++ b/src/apb_test.sv @@ -23,10 +23,13 @@ package apb_test; typedef logic [ADDR_WIDTH-1:0] addr_t; typedef logic [DATA_WIDTH-1:0] data_t; typedef logic [STRB_WIDTH-1:0] strb_t; - virtual APB_DV apb; + virtual APB_DV #( + .ADDR_WIDTH(ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH) + ) apb; semaphore lock; - function new(virtual APB_DV apb); + function new(virtual APB_DV #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH)) apb); this.apb = apb; this.lock = new(1); endfunction @@ -93,13 +96,13 @@ package apb_test; while (!lock.try_get()) begin cycle_end(); end - apb.paddr <= #TA addr; - apb.pwdata <= #TA data; - apb.pstrb <= #TA strb; - apb.pwrite <= #TA 1'b1; - apb.psel[psel_idx] <= #TA 1'b1; + apb.paddr <= #TA addr; + apb.pwdata <= #TA data; + apb.pstrb <= #TA strb; + apb.pwrite <= #TA 1'b1; + apb.psel <= #TA 1'b1; cycle_end(); - apb.penable <= #TA 1'b1; + apb.penable <= #TA 1'b1; cycle_start(); while (!apb.pready) begin cycle_end(); @@ -111,7 +114,7 @@ package apb_test; apb.pwdata <= #TA '0; apb.pstrb <= #TA '0; apb.pwrite <= #TA 1'b0; - apb.psel <= #TA '0; + apb.psel <= #TA 1'b0; apb.penable <= #TA 1'b0; lock.put(); endtask diff --git a/test/tb_apb_ro_regs.sv b/test/tb_apb_ro_regs.sv new file mode 100644 index 0000000..7fe045b --- /dev/null +++ b/test/tb_apb_ro_regs.sv @@ -0,0 +1,121 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Wolfgang Roenninger + +// Description: Testbench for `apb_ro_regs`. + +`include "apb/assign.svh" + +module tb_apb_ro_regs; + + localparam int unsigned NoApbRegs = 32'd10; + + localparam int unsigned ApbAddrWidth = 32'd32; + localparam int unsigned ApbDataWidth = 32'd27; + localparam int unsigned RegDataWidth = 32'd16; + + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + + typedef logic [ApbAddrWidth-1:0] apb_addr_t; + typedef logic [ApbDataWidth-1:0] apb_data_t; + typedef logic [RegDataWidth-1:0] reg_data_t; + + + + logic clk; + logic rst_n; + logic done; + apb_addr_t base_addr; + reg_data_t [NoApbRegs-1:0] reg_data; + + + APB_DV #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ) + ) apb_slave_dv(clk); + APB #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ) + ) apb_slave(); + `APB_ASSIGN ( apb_slave, apb_slave_dv ) + + //----------------------------------- + // Clock generator + //----------------------------------- + clk_rst_gen #( + .CLK_PERIOD ( CyclTime ), + .RST_CLK_CYCLES( 5 ) + ) i_clk_gen ( + .clk_o (clk), + .rst_no(rst_n) + ); + + apb_test::apb_driver #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ), + .TA ( ApplTime ), + .TT ( TestTime ) + ) apb_master = new(apb_slave_dv); + + initial begin : proc_apb_master + automatic apb_addr_t addr; + automatic apb_data_t data; + automatic logic resp; + + done <= 1'b0; + @(posedge rst_n); + apb_master.reset_master(); + repeat (10) @(posedge clk); + + + for (int unsigned i = 32'h0002_FF00; i < 32'h0004_0000; i++) begin + addr = apb_addr_t'(i); + apb_master.read(addr, data, resp); + $display("Read from addr: %0h", addr); + $display("Read data: %0h", data); + $display("Read resp: %0h", resp); + repeat (3) @(posedge clk); + end + + + done <= 1'b1; + + end + + initial begin : proc_end_sim + @(posedge done); + $stop(); + end + + initial begin : proc_set_const + base_addr <= apb_addr_t'(32'h0003_0000); + for (int unsigned i = 0; i < NoApbRegs; i++) begin + reg_data[i] = reg_data_t'(i); + end + end + + // Dut + apb_ro_regs_intf #( + .NO_APB_REGS ( NoApbRegs ), + .APB_ADDR_WIDTH ( ApbAddrWidth ), + .APB_DATA_WIDTH ( ApbDataWidth ), + .REG_DATA_WIDTH ( RegDataWidth ) + ) i_apb_ro_regs_dut ( + .pclk_i ( clk ), + .preset_ni ( rst_n ), + .slv ( apb_slave ), + .base_addr_i ( base_addr ), + .reg_i ( reg_data ) + ); + +endmodule From 57be85dcf26e93edf24bd513a7abb683c8e1dc40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 13 Mar 2020 10:59:01 +0100 Subject: [PATCH 30/37] Add contribution guidelines --- CHANGELOG.md | 1 + CONTRIBUTING.md | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e398579..a23f7ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Define macros for assigning APB interfaces. - Add read-only and read-write registers with APB interface. - Add basic test infrastructure for APB modules. +- Add contribution guidelines. ### Changed - Rename `APB_BUS` interface to `APB`, change its parameters to constants, and remove `in` and `out` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..404c9db --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contribution Guidelines + +## Coding Style + +All SystemVerilog code in this repository _must_ adhere to the [SystemVerilog Coding Style Guide by +lowRISC](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md) and the +following rules: + +- All module names _must_ start with `apb_`. + +- User-facing modules _must_ have SystemVerilog `struct`s as APB ports. The concrete `struct` type + _must_ be defined as `parameter` to the module. The fields of the `struct` _must_ correspond to + those defined by our [`typedef` + macros](https://github.com/pulp-platform/apb/blob/master/include/apb/typedef.svh). + +- User-facing modules _may_ come with a variant that has SystemVerilog interfaces as APB ports. + - Such an interface variant module _must not_ implement any functionality except wiring its + interfaces to the `struct` ports of the original module. + - The name of an interface variant _must_ be the name of the original module suffixed by `_intf`. + - The parameters of an interface variant must be formatted `ALL_CAPS`. + + +## Collaboration Guidelines + +We follow [`pulp-platform`'s Collaboration +Guidelines](https://github.com/pulp-platform/style-guidelines/blob/master/CONTRIBUTING.md). From be34ee299170739b4e27096a6c81417bb79d5864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 21 Feb 2020 15:22:53 +0100 Subject: [PATCH 31/37] apb_ro_regs: Add testbench --- src/apb_ro_regs.sv | 22 ++++++---- test/tb_apb_ro_regs.sv | 88 +++++++++++++++++++++++++++++++------ test/tb_apb_ro_regs.wave.do | 27 ++++++++++++ 3 files changed, 116 insertions(+), 21 deletions(-) create mode 100644 test/tb_apb_ro_regs.wave.do diff --git a/src/apb_ro_regs.sv b/src/apb_ro_regs.sv index 7a37ba6..6fc5ff6 100644 --- a/src/apb_ro_regs.sv +++ b/src/apb_ro_regs.sv @@ -18,6 +18,7 @@ module apb_ro_regs #( parameter int unsigned NoApbRegs = 32'd0, // number of read only registers parameter int unsigned ApbAddrWidth = 32'd0, // address width of `req_i.paddr` + parameter int unsigned ApbDataWidth = 32'd0, // data width of the `req_i.pwdata` & `resp_o.prdata` parameter int unsigned RegDataWidth = 32'd0, // data width of the registers parameter type req_t = logic, // APB4 request type parameter type resp_t = logic, // APB4 response type @@ -35,7 +36,8 @@ module apb_ro_regs #( input reg_data_t [NoApbRegs-1:0] reg_i ); localparam int unsigned IdxWidth = (NoApbRegs > 32'd1) ? $clog2(NoApbRegs) : 32'd1; - typedef logic [IdxWidth-1:0] idx_t; + typedef logic [IdxWidth-1:0] idx_t; + typedef logic [ApbAddrWidth-1:0] apb_data_t; typedef struct packed { int unsigned idx; apb_addr_t start_addr; @@ -59,16 +61,15 @@ module apb_ro_regs #( always_comb begin resp_o = '{ pready: req_i.psel & req_i.penable, - prdata: '0, + prdata: apb_data_t'(32'h0BAD_B10C), pslverr: apb_pkg::RESP_OKAY }; if (req_i.psel) begin if (req_i.pwrite || !decode_valid) begin // Error response on writes and decode errors resp_o.pslverr = apb_pkg::RESP_SLVERR; - resp_o.prdata = reg_data_t'(32'h0BAD_B10C); end else begin - resp_o.prdata = reg_i[reg_idx]; + resp_o.prdata = apb_data_t'(reg_i[reg_idx]); end end end @@ -96,14 +97,18 @@ module apb_ro_regs #( else $fatal(1, "The number of registers must be at least 1!"); assert (ApbAddrWidth > 32'd2) else $fatal(1, "ApbAddrWidth is not wide enough, has to be at least 3 bit wide!"); + assert ($bits(req_i.paddr) == ApbAddrWidth) + else $fatal(1, "AddrWidth does not match req_i.paddr!"); + assert (ApbDataWidth == $bits(resp_o.prdata)) + else $fatal(1, "ApbDataWidth has to be: ApbDataWidth == $bits(req_i.prdata)!"); + assert (ApbDataWidth > 32'd0 && ApbDataWidth <= 32'd32) + else $fatal(1, "ApbDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); + assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) + else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); assert (RegDataWidth > 32'd0 && RegDataWidth <= 32'd32) else $fatal(1, "RegDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); assert (RegDataWidth <= $bits(resp_o.prdata)) else $fatal(1, "RegDataWidth has to be: RegDataWidth <= $bits(req_i.prdata)!"); - assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) - else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); - assert ($bits(req_i.paddr) == ApbAddrWidth) - else $fatal(1, "AddrWidth does not match req_i.paddr!"); end `endif // pragma translate_on @@ -145,6 +150,7 @@ module apb_ro_regs_intf #( apb_ro_regs #( .NoApbRegs ( NO_APB_REGS ), .ApbAddrWidth ( APB_ADDR_WIDTH ), + .ApbDataWidth ( APB_DATA_WIDTH ), .RegDataWidth ( REG_DATA_WIDTH ), .req_t ( apb_req_t ), .resp_t ( apb_resp_t ) diff --git a/test/tb_apb_ro_regs.sv b/test/tb_apb_ro_regs.sv index 7fe045b..29c8f76 100644 --- a/test/tb_apb_ro_regs.sv +++ b/test/tb_apb_ro_regs.sv @@ -16,10 +16,11 @@ module tb_apb_ro_regs; - localparam int unsigned NoApbRegs = 32'd10; + localparam int unsigned NoApbRegs = 32'd342; localparam int unsigned ApbAddrWidth = 32'd32; localparam int unsigned ApbDataWidth = 32'd27; + localparam int unsigned ApbStrbWidth = cf_math_pkg::ceil_div(ApbDataWidth, 8); localparam int unsigned RegDataWidth = 32'd16; localparam time CyclTime = 10ns; @@ -28,14 +29,16 @@ module tb_apb_ro_regs; typedef logic [ApbAddrWidth-1:0] apb_addr_t; typedef logic [ApbDataWidth-1:0] apb_data_t; + typedef logic [ApbStrbWidth-1:0] apb_strb_t; typedef logic [RegDataWidth-1:0] reg_data_t; - + localparam apb_addr_t BaseAddr = 32'h0003_0000; + localparam apb_addr_t TestStartAddr = 32'h0002_FF00; + localparam apb_addr_t TestEndAddr = 32'h0003_0F00; logic clk; logic rst_n; logic done; - apb_addr_t base_addr; reg_data_t [NoApbRegs-1:0] reg_data; @@ -76,34 +79,93 @@ module tb_apb_ro_regs; @(posedge rst_n); apb_master.reset_master(); repeat (10) @(posedge clk); + apb_master.write( BaseAddr, apb_data_t'(32'd0000_0000), apb_strb_t'(4'hF), resp); + $display("Write addr: %0h", addr); + $display("Write data: %0h", data); + $display("Write resp: %0h", resp); + assert(resp == apb_pkg::RESP_SLVERR); - - for (int unsigned i = 32'h0002_FF00; i < 32'h0004_0000; i++) begin + for (int unsigned i = TestStartAddr; i < TestEndAddr; i++) begin addr = apb_addr_t'(i); apb_master.read(addr, data, resp); $display("Read from addr: %0h", addr); $display("Read data: %0h", data); $display("Read resp: %0h", resp); - repeat (3) @(posedge clk); + repeat ($urandom_range(0,5)) @(posedge clk); end - - done <= 1'b1; - end initial begin : proc_end_sim @(posedge done); + repeat(10) @(posedge clk); $stop(); end - initial begin : proc_set_const - base_addr <= apb_addr_t'(32'h0003_0000); + initial begin : proc_set_reg_data for (int unsigned i = 0; i < NoApbRegs; i++) begin - reg_data[i] = reg_data_t'(i); + reg_data[i] = reg_data_t'($urandom()); end end + // pragma translate_off + `ifndef VERILATOR + // Assertions to determine correct APB protocol sequencing + default disable iff (!rst_n); + // when psel is not asserted, the bus is in the idle state + sequence APB_IDLE; + !apb_slave.psel; + endsequence + + // when psel is set and penable is not, it is the setup state + sequence APB_SETUP; + apb_slave.psel && !apb_slave.penable; + endsequence + + // when psel and penable are set it is the access state + sequence APB_ACCESS; + apb_slave.psel && apb_slave.penable; + endsequence + + sequence APB_RESP_OKAY; + apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_OKAY); + endsequence + + sequence APB_RESP_SLVERR; + apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_SLVERR); + endsequence + + // APB Transfer is APB state going from setup to access + sequence APB_TRANSFER; + APB_SETUP ##1 APB_ACCESS; + endsequence + + apb_complete: assert property ( @(posedge clk) + (APB_SETUP |-> APB_TRANSFER)); + + apb_penable: assert property ( @(posedge clk) + (apb_slave.penable && apb_slave.psel && apb_slave.pready |=> (!apb_slave.penable))); + + control_stable: assert property ( @(posedge clk) + (APB_TRANSFER |-> $stable({apb_slave.pwrite, apb_slave.paddr}))); + + apb_valid: assert property ( @(posedge clk) + (APB_TRANSFER |-> ((!{apb_slave.pwrite, apb_slave.pstrb, apb_slave.paddr}) !== 1'bx))); + + write_stable: assert property ( @(posedge clk) + ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pwdata))); + + strb_stable: assert property ( @(posedge clk) + ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pstrb))); + + correct_data: assert property ( @(posedge clk) + (APB_TRANSFER and APB_RESP_OKAY and !apb_slave.pwrite) + |-> (apb_slave.prdata == reg_data[apb_slave.paddr>>2])) else + $fatal(1, "Unexpected read response!"); + `endif + // pragma translate_on + + // Dut apb_ro_regs_intf #( .NO_APB_REGS ( NoApbRegs ), @@ -114,7 +176,7 @@ module tb_apb_ro_regs; .pclk_i ( clk ), .preset_ni ( rst_n ), .slv ( apb_slave ), - .base_addr_i ( base_addr ), + .base_addr_i ( BaseAddr ), .reg_i ( reg_data ) ); diff --git a/test/tb_apb_ro_regs.wave.do b/test/tb_apb_ro_regs.wave.do new file mode 100644 index 0000000..8e5ce21 --- /dev/null +++ b/test/tb_apb_ro_regs.wave.do @@ -0,0 +1,27 @@ +log -r * +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/pclk_i +add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/preset_ni +add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/base_addr_i +add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/reg_i +add wave -noupdate -expand /tb_apb_ro_regs/i_apb_ro_regs_dut/apb_req +add wave -noupdate -expand /tb_apb_ro_regs/i_apb_ro_regs_dut/apb_resp +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {0 ns} 0} +quietly wave cursor active 0 +configure wave -namecolwidth 310 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 0 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ns +update +WaveRestoreZoom {0 ns} {20648 ns} From bdcfb7695b4f55a1ac91fd9afe47b10ae30e28cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 21 Feb 2020 15:23:30 +0100 Subject: [PATCH 32/37] apb_rw_regs: Add testbench --- src/apb_rw_regs.sv | 211 ++++++++++++++++++++++++-------- test/tb_apb_rw_regs.sv | 237 ++++++++++++++++++++++++++++++++++++ test/tb_apb_rw_regs.wave.do | 28 +++++ 3 files changed, 425 insertions(+), 51 deletions(-) create mode 100644 test/tb_apb_rw_regs.sv create mode 100644 test/tb_apb_rw_regs.wave.do diff --git a/src/apb_rw_regs.sv b/src/apb_rw_regs.sv index 41a737d..011f183 100644 --- a/src/apb_rw_regs.sv +++ b/src/apb_rw_regs.sv @@ -14,78 +14,187 @@ `include "common_cells/registers.svh" module apb_rw_regs #( - parameter int unsigned NoRegs = 0, - parameter int unsigned AddrWidth = 32'd0, - parameter int unsigned DataWidth = 32'd0, + parameter int unsigned NoApbRegs = 32'd0, // number of read only registers + parameter int unsigned ApbAddrWidth = 32'd0, // address width of `req_i.paddr` + parameter int unsigned ApbDataWidth = 32'd0, // data width of the `req_i.pwdata` & `resp_o.prdata` + parameter int unsigned RegDataWidth = 32'd0, // data width of the registers + parameter type req_t = logic, // APB4 request type + parameter type resp_t = logic, // APB4 response type // DEPENDENT PARAMETERS DO NOT OVERWRITE! - parameter int unsigned StrbWidth = cf_math_pkg::ceil_div(DataWidth, 8), - parameter type addr_t = logic[AddrWidth-1:0], - parameter type data_t = logic[DataWidth-1:0], - parameter type strb_t = logic[StrbWidth-1:0] + parameter type apb_addr_t = logic[ApbAddrWidth-1:0], + parameter type reg_data_t = logic[RegDataWidth-1:0] ) ( // APB Interface input logic pclk_i, input logic preset_ni, - input addr_t paddr_i, - input apb_pkg::prot_t pprot_i, - input logic psel_i, - input logic penable_i, - input logic pwrite_i, - input data_t pwdata_i, - input strb_t pstrb_i, - output logic pready_o, - output data_t prdata_o, - output logic pslverr_o, - + input req_t req_i, + output resp_t resp_o, // Register Interface - input data_t [NoRegs-1:0] init_i, - output data_t [NoRegs-1:0] q_o + input apb_addr_t base_addr_i, // base address of the read/write registers + input reg_data_t [NoApbRegs-1:0] reg_init_i, + output reg_data_t [NoApbRegs-1:0] reg_q_o ); - // if StrbWidth = 1 we want Word Offset = 0 - localparam int unsigned WordOffset = $clog2(StrbWidth); + localparam int unsigned IdxWidth = (NoApbRegs > 32'd1) ? $clog2(NoApbRegs) : 32'd1; + typedef logic [IdxWidth-1:0] idx_t; + typedef logic [ApbAddrWidth-1:0] apb_data_t; + typedef struct packed { + int unsigned idx; + apb_addr_t start_addr; + apb_addr_t end_addr; + } rule_t; + + // signal declarations + rule_t [NoApbRegs-1:0] addr_map; + idx_t reg_idx; + logic decode_valid; + // register signals + reg_data_t [NoApbRegs-1:0] reg_d, reg_q; + logic [NoApbRegs-1:0] reg_update; - data_t [NoRegs-1:0] reg_d, reg_q; - logic reg_update; + // generate address map for the registers + for (genvar i = 0; i < NoApbRegs; i++) begin: gen_reg_addr_map + assign addr_map[i] = '{ + idx: unsigned'(i), + start_addr: base_addr_i + apb_addr_t'( i * 32'd4), + end_addr: base_addr_i + apb_addr_t'((i+32'd1) * 32'd4) + }; + end always_comb begin + // default assignments reg_d = reg_q; - reg_update = 1'b0; - prdata_o = data_t'(32'h0BAD_B10C); - pslverr_o = apb_pkg::RESP_OKAY; - if (psel_i) begin - automatic logic [AddrWidth-WordOffset-1:0] word_addr = paddr_i >> WordOffset; - if (word_addr >= NoRegs) begin - // Error response to accesses that are out of range - pslverr_o = apb_pkg::RESP_SLVERR; + reg_update = '0; + resp_o = '{ + pready: req_i.psel & req_i.penable, + prdata: apb_data_t'(32'h0BAD_B10C), + pslverr: apb_pkg::RESP_OKAY + }; + if (req_i.psel) begin + if (!decode_valid) begin + // Error response on decode errors + resp_o.pslverr = apb_pkg::RESP_SLVERR; end else begin - if (pwrite_i) begin - for (int unsigned i = 0; i < DataWidth; i++) begin - if (pstrb_i[i/8]) begin - reg_d[word_addr][i] = pwdata_i[i]; + if (req_i.pwrite) begin + for (int unsigned i = 0; i < RegDataWidth; i++) begin + if (req_i.pstrb) begin + reg_d[reg_idx][i] = req_i.pwdata[i]; end end - reg_update = |pstrb_i; // only update register when a write strobe is set + reg_update[reg_idx] = |req_i.pstrb; end else begin - prdata_o = reg_q[word_addr]; + resp_o.prdata = apb_data_t'(reg_q[reg_idx]); end end end end - assign pready_o = psel_i & penable_i; - assign q_o = reg_q; + // output assignment + assign reg_q_o = reg_q; - `FFLARN(reg_q, reg_d, reg_update, init_i, pclk_i, preset_ni) - -// Validate parameters. -// pragma translate_off -`ifndef VERILATOR - initial begin: p_assertions - assert (NoRegs > 0) else $fatal(1, "The number of registers must be at least 1!"); - assert (AddrWidth > WordOffset) else $fatal(1, "AddrWidth is not wide enough!"); - assert (DataWidth > 0) else $fatal(1, "DataWidth has to be > 0!"); + for (genvar i = 0; i < NoApbRegs; i++) begin : gen_rw_regs + `FFLARN(reg_q[i], reg_d[i], reg_update[i], reg_init_i[i], pclk_i, preset_ni) end -`endif -// pragma translate_on + addr_decode #( + .NoIndices ( NoApbRegs ), + .NoRules ( NoApbRegs ), + .addr_t ( apb_addr_t ), + .rule_t ( rule_t ) + ) i_addr_decode ( + .addr_i ( req_i.paddr ), + .addr_map_i ( addr_map ), + .idx_o ( reg_idx ), + .dec_valid_o ( decode_valid ), + .dec_error_o ( /*not used*/ ), + .en_default_idx_i ( '0 ), + .default_idx_i ( '0 ) + ); + + // Validate parameters. + // pragma translate_off + `ifndef VERILATOR + initial begin: p_assertions + assert (NoApbRegs > 32'd0) + else $fatal(1, "The number of registers must be at least 1!"); + assert (ApbAddrWidth > 32'd2) + else $fatal(1, "ApbAddrWidth is not wide enough, has to be at least 3 bit wide!"); + assert ($bits(req_i.paddr) == ApbAddrWidth) + else $fatal(1, "AddrWidth does not match req_i.paddr!"); + assert (ApbDataWidth == $bits(resp_o.prdata)) + else $fatal(1, "ApbDataWidth has to be: ApbDataWidth == $bits(req_i.prdata)!"); + assert (ApbDataWidth > 32'd0 && ApbDataWidth <= 32'd32) + else $fatal(1, "ApbDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); + assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) + else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); + assert (RegDataWidth > 32'd0 && RegDataWidth <= 32'd32) + else $fatal(1, "RegDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); + assert (RegDataWidth <= $bits(resp_o.prdata)) + else $fatal(1, "RegDataWidth has to be: RegDataWidth <= $bits(req_i.prdata)!"); + end + `endif + // pragma translate_on +endmodule + +`include "apb/assign.svh" +`include "apb/typedef.svh" + +module apb_rw_regs_intf #( + parameter int unsigned NO_APB_REGS = 32'd0, // number of read only registers + parameter int unsigned APB_ADDR_WIDTH = 32'd0, // address width of `paddr` + parameter int unsigned APB_DATA_WIDTH = 32'd0, // data width of the registers + parameter int unsigned REG_DATA_WIDTH = 32'd0, + // DEPENDENT PARAMETERS DO NOT OVERWRITE! + parameter type apb_addr_t = logic[APB_ADDR_WIDTH-1:0], + parameter type reg_data_t = logic[REG_DATA_WIDTH-1:0] +) ( + // APB Interface + input logic pclk_i, + input logic preset_ni, + APB.Slave slv, + // Register Interface + input apb_addr_t base_addr_i, // base address of the read only registers + input reg_data_t [NO_APB_REGS-1:0] reg_init_i, // initalisation value for the registers + output reg_data_t [NO_APB_REGS-1:0] reg_q_o +); + localparam int unsigned APB_STRB_WIDTH = cf_math_pkg::ceil_div(APB_DATA_WIDTH, 8); + typedef logic [APB_DATA_WIDTH-1:0] apb_data_t; + typedef logic [APB_STRB_WIDTH-1:0] apb_strb_t; + + `APB_TYPEDEF_REQ_T ( apb_req_t, apb_addr_t, apb_data_t, apb_strb_t ) + `APB_TYPEDEF_RESP_T ( apb_resp_t, apb_data_t ) + + apb_req_t apb_req; + apb_resp_t apb_resp; + + `APB_ASSIGN_TO_REQ ( apb_req, slv ) + `APB_ASSIGN_FROM_RESP ( slv, apb_resp ) + + apb_rw_regs #( + .NoApbRegs ( NO_APB_REGS ), + .ApbAddrWidth ( APB_ADDR_WIDTH ), + .ApbDataWidth ( APB_DATA_WIDTH ), + .RegDataWidth ( REG_DATA_WIDTH ), + .req_t ( apb_req_t ), + .resp_t ( apb_resp_t ) + ) i_apb_rw_regs ( + .pclk_i, + .preset_ni, + .req_i ( apb_req ), + .resp_o ( apb_resp ), + .base_addr_i, + .reg_init_i, + .reg_q_o + ); + + // Validate parameters. + // pragma translate_off + `ifndef VERILATOR + initial begin: p_assertions + assert (APB_ADDR_WIDTH == $bits(slv.paddr)) + else $fatal(1, "APB_ADDR_WIDTH does not match slv interface!"); + assert (APB_DATA_WIDTH == $bits(slv.pwdata)) + else $fatal(1, "APB_DATA_WIDTH does not match slv interface!"); + end + `endif + // pragma translate_on endmodule diff --git a/test/tb_apb_rw_regs.sv b/test/tb_apb_rw_regs.sv new file mode 100644 index 0000000..ad6bc4d --- /dev/null +++ b/test/tb_apb_rw_regs.sv @@ -0,0 +1,237 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Wolfgang Roenninger + +// Description: Testbench for `apb_rw_regs`. +// Step 1: Read over a range of addresses, where the registers are inbetween. +// Step 2: Random reads and writes to the different registers. +// Assertions provide checking for correct functionality. + +`include "apb/assign.svh" + +module tb_apb_rw_regs; + + localparam int unsigned NoApbRegs = 32'd342; + + localparam int unsigned ApbAddrWidth = 32'd32; + localparam int unsigned ApbDataWidth = 32'd27; + localparam int unsigned ApbStrbWidth = cf_math_pkg::ceil_div(ApbDataWidth, 8); + localparam int unsigned RegDataWidth = 32'd16; + + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + localparam int unsigned NoRandAccesses = 32'd10000; + + typedef logic [ApbAddrWidth-1:0] apb_addr_t; + typedef logic [ApbDataWidth-1:0] apb_data_t; + typedef logic [ApbStrbWidth-1:0] apb_strb_t; + typedef logic [RegDataWidth-1:0] reg_data_t; + + localparam apb_addr_t BaseAddr = 32'h0003_0000; + localparam apb_addr_t TestStartAddr = 32'h0002_FF00; + localparam apb_addr_t TestEndAddr = 32'h0003_0F00; + + logic clk; + logic rst_n; + logic done; + apb_addr_t last_addr; + reg_data_t [NoApbRegs-1:0] reg_data, reg_init, reg_compare; + + + APB_DV #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ) + ) apb_slave_dv(clk); + APB #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ) + ) apb_slave(); + `APB_ASSIGN ( apb_slave, apb_slave_dv ) + + //----------------------------------- + // Clock generator + //----------------------------------- + clk_rst_gen #( + .CLK_PERIOD ( CyclTime ), + .RST_CLK_CYCLES( 5 ) + ) i_clk_gen ( + .clk_o (clk), + .rst_no(rst_n) + ); + + apb_test::apb_driver #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ), + .TA ( ApplTime ), + .TT ( TestTime ) + ) apb_master = new(apb_slave_dv); + + initial begin : proc_apb_master + automatic apb_addr_t addr; + automatic apb_data_t data; + automatic apb_strb_t strb; + automatic logic resp; + automatic bit w; + automatic reg_data_t init_val; + // initialize reset values and golden model + for (int unsigned i = 0; i < NoApbRegs; i++) begin + init_val = reg_data_t'($urandom()); + reg_init[i] = init_val; + reg_compare = init_val; + end + done <= 1'b0; + + // reset dut + @(posedge rst_n); + apb_master.reset_master(); + repeat (10) @(posedge clk); + + // First test + apb_master.write( BaseAddr, apb_data_t'(32'd0000_0000), apb_strb_t'(4'hF), resp); + $display("Write addr: %0h", addr); + $display("Write data: %0h strb: %0h", data, strb); + $display("Write resp: %0h", resp); + assert(resp == apb_pkg::RESP_SLVERR); + + // Step 1 + for (int unsigned i = TestStartAddr; i < TestEndAddr; i++) begin + addr = apb_addr_t'(i); + apb_master.read(addr, data, resp); + $display("Read from addr: %0h", addr); + $display("Read data: %0h", data); + $display("Read resp: %0h", resp); + repeat ($urandom_range(0,5)) @(posedge clk); + end + + // Step 2 + for (int unsigned i = 0; i < NoRandAccesses; i++) begin + w = bit'($urandom()); + addr = apb_addr_t'($urandom_range(TestStartAddr, TestEndAddr)); + data = apb_data_t'($urandom()); + strb = apb_strb_t'($urandom()); + if (w) begin + apb_master.write( addr, data, strb, resp); + $display("Write addr: %0h", addr); + $display("Write data: %0h strb: %0h", data, strb); + $display("Write resp: %0h", resp); + if (resp == apb_pkg::RESP_OKAY) begin + // update golden model + for (int unsigned j = 0; j < RegDataWidth; j++) begin + if (strb[j/8]) begin + reg_compare[j] = data[j]; + end + end + end + end else begin + apb_master.read(addr, data, resp); + $display("Read from addr: %0h", addr); + $display("Read data: %0h", data); + $display("Read resp: %0h", resp); + end + repeat ($urandom_range(0,5)) @(posedge clk); + end + + done <= 1'b1; + end + + initial begin : proc_end_sim + @(posedge done); + repeat(10) @(posedge clk); + $stop(); + end + + // one cycle delayed addr + always_ff @(posedge clk or negedge rst_n) begin : proc_last_addr_reg + if(~rst_n) begin + last_addr <= '0; + end else begin + last_addr <= apb_slave.paddr; + end + end + + + // pragma translate_off + `ifndef VERILATOR + // Assertions to determine correct APB protocol sequencing + default disable iff (!rst_n); + // when psel is not asserted, the bus is in the idle state + sequence APB_IDLE; + !apb_slave.psel; + endsequence + + // when psel is set and penable is not, it is the setup state + sequence APB_SETUP; + apb_slave.psel && !apb_slave.penable; + endsequence + + // when psel and penable are set it is the access state + sequence APB_ACCESS; + apb_slave.psel && apb_slave.penable; + endsequence + + sequence APB_RESP_OKAY; + apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_OKAY); + endsequence + + sequence APB_RESP_SLVERR; + apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_SLVERR); + endsequence + + // APB Transfer is APB state going from setup to access + sequence APB_TRANSFER; + APB_SETUP ##1 APB_ACCESS; + endsequence + + apb_complete: assert property ( @(posedge clk) + (APB_SETUP |-> APB_TRANSFER)); + + apb_penable: assert property ( @(posedge clk) + (apb_slave.penable && apb_slave.psel && apb_slave.pready |=> (!apb_slave.penable))); + + control_stable: assert property ( @(posedge clk) + (APB_TRANSFER |-> $stable({apb_slave.pwrite, apb_slave.paddr}))); + + apb_valid: assert property ( @(posedge clk) + (APB_TRANSFER |-> ((!{apb_slave.pwrite, apb_slave.pstrb, apb_slave.paddr}) !== 1'bx))); + + write_stable: assert property ( @(posedge clk) + ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pwdata))); + + strb_stable: assert property ( @(posedge clk) + ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pstrb))); + + correct_rdata: assert property ( @(posedge clk) + (APB_TRANSFER and APB_RESP_OKAY and !apb_slave.pwrite) + |-> (apb_slave.prdata == apb_data_t'(reg_compare[apb_slave.paddr>>2]))) else + $fatal(1, "Unexpected read response!"); + correct_wdata: assert property ( @(posedge clk) + (APB_TRANSFER and APB_RESP_OKAY and apb_slave.pwrite) + |=> (reg_data[last_addr>>2] == reg_compare[last_addr>>2])) else + $fatal(1, "Unexpected write output!"); + `endif + // pragma translate_on + + // Dut + apb_rw_regs_intf #( + .NO_APB_REGS ( NoApbRegs ), + .APB_ADDR_WIDTH ( ApbAddrWidth ), + .APB_DATA_WIDTH ( ApbDataWidth ), + .REG_DATA_WIDTH ( RegDataWidth ) + ) i_apb_rw_regs_dut ( + .pclk_i ( clk ), + .preset_ni ( rst_n ), + .slv ( apb_slave ), + .base_addr_i ( BaseAddr ), + .reg_init_i ( reg_init ), + .reg_q_o ( reg_data ) + ); +endmodule diff --git a/test/tb_apb_rw_regs.wave.do b/test/tb_apb_rw_regs.wave.do new file mode 100644 index 0000000..4bda443 --- /dev/null +++ b/test/tb_apb_rw_regs.wave.do @@ -0,0 +1,28 @@ +log -r * +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/pclk_i +add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/preset_ni +add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/base_addr_i +add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/reg_init_i +add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/reg_q_o +add wave -noupdate -expand /tb_apb_rw_regs/i_apb_rw_regs_dut/apb_req +add wave -noupdate -expand /tb_apb_rw_regs/i_apb_rw_regs_dut/apb_resp +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {0 ns} 0} +quietly wave cursor active 0 +configure wave -namecolwidth 310 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 0 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ns +update +WaveRestoreZoom {0 ns} {20648 ns} From 87470664e4cd7c20e2bbcdd41d01a907583efc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 21 Feb 2020 15:24:14 +0100 Subject: [PATCH 33/37] Add simulation and synthesis scripts --- Bender.yml | 7 +++ scripts/compile_vsim.sh | 21 +++++++ scripts/run_vsim.sh | 28 +++++++++ scripts/synth.sh | 28 +++++++++ test/synth_bench.sv | 131 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 215 insertions(+) create mode 100755 scripts/compile_vsim.sh create mode 100755 scripts/run_vsim.sh create mode 100755 scripts/synth.sh create mode 100644 test/synth_bench.sv diff --git a/Bender.yml b/Bender.yml index 502bb56..28c2f33 100644 --- a/Bender.yml +++ b/Bender.yml @@ -31,3 +31,10 @@ sources: - target: test files: - test/tb_apb_ro_regs.sv + - test/tb_apb_rw_regs.sv + + - target: synth_test + files: + # Level 0 + - test/synth_bench.sv + diff --git a/scripts/compile_vsim.sh b/scripts/compile_vsim.sh new file mode 100755 index 0000000..6fdc439 --- /dev/null +++ b/scripts/compile_vsim.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright (c) 2014-2020 ETH Zurich, University of Bologna +# +# Copyright and related rights are licensed under the Solderpad Hardware +# License, Version 0.51 (the "License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +# or agreed to in writing, software, hardware and materials distributed under +# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# Fabian Schuiki +# Andreas Kurth +# Wolfgang Roenninger + +set -e + +bender script vsim -t test --vlog-arg="-svinputport=compat" --vlog-arg="-override_timescale 1ns/1ps" > compile.tcl +echo 'return 0' >> compile.tcl +vsim -c -do 'exit -code [source compile.tcl]' diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh new file mode 100755 index 0000000..63243de --- /dev/null +++ b/scripts/run_vsim.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright (c) 2014-2020 ETH Zurich, University of Bologna +# +# Copyright and related rights are licensed under the Solderpad Hardware +# License, Version 0.51 (the "License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +# or agreed to in writing, software, hardware and materials distributed under +# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# Fabian Schuiki +# Andreas Kurth +# Wolfgang Roenninger + +set -e +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + +[ ! -z "$VSIM" ] || VSIM=vsim + +call_vsim() { + echo "run -all" | $VSIM "$@" | tee vsim.log 2>&1 + grep "Errors: 0," vsim.log +} + +call_vsim tb_apb_ro_regs -64 -t 1ns -coverage -lib rtl -voptargs="+acc +cover=bcesfx" +call_vsim tb_apb_rw_regs -64 -t 1ns -coverage -lib rtl -voptargs="+acc +cover=bcesfx" diff --git a/scripts/synth.sh b/scripts/synth.sh new file mode 100755 index 0000000..2658241 --- /dev/null +++ b/scripts/synth.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright (c) 2014-2018 ETH Zurich, University of Bologna +# +# Copyright and related rights are licensed under the Solderpad Hardware +# License, Version 0.51 (the "License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +# or agreed to in writing, software, hardware and materials distributed under +# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# Fabian Schuiki +# Andreas Kurth +# Wolfgang Roenninger + +set -e +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + +[ ! -z "$SYNOPSYS_DC" ] || SYNOPSYS_DC="synopsys dc_shell -64" + +echo 'remove_design -all' > ./synth.tcl +bender script synopsys -t synth_test >> ./synth.tcl +echo 'elaborate synth_bench' >> ./synth.tcl + +cat ./synth.tcl | $SYNOPSYS_DC | tee synth.log 2>&1 +grep -i "warning:" synth.log || true +! grep -i "error:" synth.log diff --git a/test/synth_bench.sv b/test/synth_bench.sv new file mode 100644 index 0000000..d81552e --- /dev/null +++ b/test/synth_bench.sv @@ -0,0 +1,131 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Wolfgang Roenninger + +/// A synthesis test bench which instantiates various APB modules. +module synth_bench ( + input logic clk_i, + input logic rst_ni +); + + localparam int unsigned APB_ADDR_WIDTH[6] = {3, 7, 16, 27, 32}; + localparam int unsigned APB_DATA_WIDTH[6] = {1, 2, 16, 27, 32}; + localparam int unsigned REG_DATA_WIDTH[6] = {1, 4, 13, 27, 32}; + + // APB RO REGS + for (genvar i = 0; i < 5; i++) begin : gen_ro_addr + for (genvar j = 0; j < 5; j++) begin : gen_ro_data + for (genvar k = 0; k < 5; k++) begin : gen_ro_width + for (genvar l = 0; l < 4; l++) begin : gen_no_ro_regs + localparam int unsigned NoApbRegs = 2**l; + localparam int unsigned RegDataWidth = (APB_DATA_WIDTH[k] > REG_DATA_WIDTH[k]) ? + APB_DATA_WIDTH[k] : REG_DATA_WIDTH[k]; + synth_apb_ro_regs #( + .NoApbRegs ( NoApbRegs ), + .ApbAddrWidth( APB_ADDR_WIDTH[i] ), + .ApbDataWidth( APB_DATA_WIDTH[j] ), + .RegDataWidth( RegDataWidth ) + ) i_synth_ro_regs (.*); + end + end + end + end + + // APB RW REGS + for (genvar i = 0; i < 6; i++) begin : gen_rw_addr + for (genvar j = 0; j < 6; j++) begin : gen_rw_data + for (genvar k = 0; k < 4; k++) begin : gen_rw_width + for (genvar l = 0; l < 4; l++) begin : gen_no_rw_regs + localparam int unsigned NoApbRegs = 2**l; + localparam int unsigned RegDataWidth = (APB_DATA_WIDTH[k] > REG_DATA_WIDTH[k]) ? + APB_DATA_WIDTH[k] : REG_DATA_WIDTH[k]; + synth_apb_rw_regs #( + .NoApbRegs ( NoApbRegs ), + .ApbAddrWidth( APB_ADDR_WIDTH[i] ), + .ApbDataWidth( APB_DATA_WIDTH[j] ), + .RegDataWidth( RegDataWidth ) + ) i_synth_ro_regs (.*); + end + end + end + end +endmodule + + +module synth_apb_ro_regs #( + parameter int unsigned NoApbRegs = 32'd0, + parameter int unsigned ApbAddrWidth = 32'd0, + parameter int unsigned ApbDataWidth = 32'd0, + parameter int unsigned RegDataWidth = 32'd0 +) ( + input logic clk_i, + input logic rst_ni +); + typedef logic [ApbAddrWidth-1:0] apb_addr_t; + typedef logic [RegDataWidth-1:0] reg_data_t; + + APB #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ) + ) apb_slave(); + + apb_addr_t base_addr; + reg_data_t [NoApbRegs-1:0] register; + + apb_ro_regs_intf #( + .NO_APB_REGS ( NoApbRegs ), + .APB_ADDR_WIDTH ( ApbAddrWidth ), + .APB_DATA_WIDTH ( ApbDataWidth ), + .REG_DATA_WIDTH ( RegDataWidth ) + ) i_apb_ro_reg_intf ( + .pclk_i ( clk_i ), + .preset_ni ( rst_ni ), + .slv ( apb_slave ), + .base_addr_i ( base_addr ), + .reg_i ( register ) + ); +endmodule + + +module synth_apb_rw_regs #( + parameter int unsigned NoApbRegs = 32'd0, + parameter int unsigned ApbAddrWidth = 32'd0, + parameter int unsigned ApbDataWidth = 32'd0, + parameter int unsigned RegDataWidth = 32'd0 +) ( + input logic clk_i, + input logic rst_ni +); + typedef logic [ApbAddrWidth-1:0] apb_addr_t; + typedef logic [RegDataWidth-1:0] reg_data_t; + + APB #( + .ADDR_WIDTH ( ApbAddrWidth ), + .DATA_WIDTH ( ApbDataWidth ) + ) apb_slave(); + + apb_addr_t base_addr; + reg_data_t [NoApbRegs-1:0] register, reg_init; + + apb_rw_regs_intf #( + .NO_APB_REGS ( NoApbRegs ), + .APB_ADDR_WIDTH ( ApbAddrWidth ), + .APB_DATA_WIDTH ( ApbDataWidth ), + .REG_DATA_WIDTH ( RegDataWidth ) + ) i_apb_rw_reg_intf ( + .pclk_i ( clk_i ), + .preset_ni ( rst_ni ), + .slv ( apb_slave ), + .base_addr_i ( base_addr ), + .reg_init_i ( reg_init ), + .reg_q_o ( register ) + ); +endmodule From 1636d253a801ce304188d6a02d55ec023d0a92d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 21 Feb 2020 15:36:12 +0100 Subject: [PATCH 34/37] apb_bus: Remove module due to outdated ports --- src/apb_bus.sv | 122 ------------------------------------------------- 1 file changed, 122 deletions(-) delete mode 100644 src/apb_bus.sv diff --git a/src/apb_bus.sv b/src/apb_bus.sv deleted file mode 100644 index c28562b..0000000 --- a/src/apb_bus.sv +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -// APB Bus with Single Master and Multiple Slave Interfaces -// Each slave is accessible at exactly one address segment, and the address ranges of no two slaves -// may overlap. If the input address of this bus is not in any slave address segment, the bus -// responds with slverr; otherwise, this bus feeds all signals through to the slave. The slaves see -// offset-compensated addresses, e.g., if this bus gets ADDR_BEGIN[i] as input address, slave i will -// get address 0. -module apb_bus #( - // Number of slaves. - parameter int unsigned N_SLV = 0, - // Address ranges of the slaves. Slave i is mapped in the inclusive interval from ADDR_BEGIN[i] to - // ADDR_END[i]. - parameter logic [N_SLV-1:0][31:0] ADDR_BEGIN = '0, - parameter logic [N_SLV-1:0][31:0] ADDR_END = '0 -) ( - // Input - input logic pclk_i, - input logic preset_ni, - input logic [31:0] paddr_i, - input logic [2:0] pprot_i, - input logic psel_i, - input logic penable_i, - input logic pwrite_i, - input logic [31:0] pwdata_i, - input logic [3:0] pstrb_i, - output logic pready_o, - output logic [31:0] prdata_o, - output logic pslverr_o, - - // Outputs - output logic [N_SLV-1:0] pclk_o, - output logic [N_SLV-1:0] preset_no, - output logic [N_SLV-1:0][31:0] paddr_o, - output logic [N_SLV-1:0] [2:0] pprot_o, - output logic [N_SLV-1:0] psel_o, - output logic [N_SLV-1:0] penable_o, - output logic [N_SLV-1:0] pwrite_o, - output logic [N_SLV-1:0][31:0] pwdata_o, - output logic [N_SLV-1:0] [3:0] pstrb_o, - input logic [N_SLV-1:0] pready_i, - input logic [N_SLV-1:0][31:0] prdata_i, - input logic [N_SLV-1:0] pslverr_i -); - - logic [$clog2(N_SLV)-1:0] sel_idx; - logic dec_err; - - for (genvar i = 0; i < N_SLV; i++) begin: gen_oup_demux - assign pclk_o[i] = pclk_i; - assign preset_no[i] = preset_ni; - assign paddr_o[i] = paddr_i - ADDR_BEGIN[i]; - assign pprot_o[i] = pprot_i; - assign psel_o[i] = psel_i & (paddr_i >= ADDR_BEGIN[i] && paddr_i <= ADDR_END[i]); - assign penable_o[i] = penable_i; - assign pwrite_o[i] = pwrite_i; - assign pwdata_o[i] = pwdata_i; - assign pstrb_o[i] = pstrb_i; - end - - assign dec_err = psel_i & ~(|psel_o); - - onehot_to_bin #(.ONEHOT_WIDTH(N_SLV)) i_sel_idx ( - .onehot (psel_o), - .bin (sel_idx) - ); - - always_comb begin - if (psel_i) begin - if (dec_err) begin - pready_o = 1'b1; - prdata_o = '0; - pslverr_o = 1'b1; - end else begin - pready_o = pready_i[sel_idx]; - prdata_o = prdata_i[sel_idx]; - pslverr_o = pslverr_i[sel_idx]; - end - end else begin // !psel_i - pready_o = 1'b0; - prdata_o = '0; - pslverr_o = 1'b0; - end - end - -// Validate parameters. -// pragma translate_off -`ifndef VERILATOR - initial begin: p_assertions - assert (N_SLV >= 1) else $fatal(1, "The number of slave ports must be at least 1!"); - end - for (genvar i = 0; i < N_SLV; i++) begin: gen_assert_addr_outer - initial begin - assert (ADDR_BEGIN[i] <= ADDR_END[i]) - else $fatal(1, "Invalid address range for slave %0d", i); - end - for (genvar j = 0; j < N_SLV; j++) begin: gen_assert_addr_inner - initial begin - if (i != j) begin - if (ADDR_BEGIN[j] >= ADDR_BEGIN[i]) begin - assert (ADDR_BEGIN[j] > ADDR_END[i]) - else $fatal("Address range of slaves %0d and %0d overlap!", i, j); - end else begin - assert (ADDR_END[j] < ADDR_BEGIN[i]) - else $fatal("Address range of slaves %0d and %0d overlap!", i, j); - end - end - end - end - end -`endif -// pragma translate_on - -endmodule From 58ca285120dd734d2f9a450e36a95a2a492a308d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 13 Mar 2020 10:51:58 +0100 Subject: [PATCH 35/37] Changelog: Update --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a23f7ea..51f4d8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,15 +8,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Add clocked `APB_DV` interface for design verification. +- Define macros for APB typedefs. - Define macros for assigning APB interfaces. - Add read-only and read-write registers with APB interface. - Add basic test infrastructure for APB modules. - Add contribution guidelines. +- Add RTL testbenches for modules. +- Add synthesis and simulation scripts. +- `synth_bench`: add synthesis bench. ### Changed - Rename `APB_BUS` interface to `APB`, change its parameters to constants, and remove `in` and `out` modports. - `apb_ro_regs`: Use of `addr_decode` module for indexing and change to structs. +- `apb_rw_regs`: Use of `addr_decode` module for indexing and change to structs. + +### Fixed + ## 0.1.0 - 2018-09-12 ### Changed From c0e0f93cb861564fea16151f499ac8e18a58a4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Tue, 25 Feb 2020 14:25:19 +0100 Subject: [PATCH 36/37] apb_regs: Merge `apb_rw_regs` and `apb_ro_regs` into `apb_regs` --- Bender.yml | 7 +- CHANGELOG.md | 7 +- README.md | 10 +- scripts/run_vsim.sh | 3 +- src/apb_regs.sv | 260 ++++++++++++++++++ src/apb_ro_regs.sv | 177 ------------ src/apb_rw_regs.sv | 200 -------------- test/synth_bench.sv | 86 ++---- test/{tb_apb_rw_regs.sv => tb_apb_regs.sv} | 59 ++-- ...pb_rw_regs.wave.do => tb_apb_regs.wave.do} | 15 +- test/tb_apb_ro_regs.sv | 183 ------------ test/tb_apb_ro_regs.wave.do | 27 -- 12 files changed, 333 insertions(+), 701 deletions(-) create mode 100644 src/apb_regs.sv delete mode 100644 src/apb_ro_regs.sv delete mode 100644 src/apb_rw_regs.sv rename test/{tb_apb_rw_regs.sv => tb_apb_regs.sv} (80%) rename test/{tb_apb_rw_regs.wave.do => tb_apb_regs.wave.do} (56%) delete mode 100644 test/tb_apb_ro_regs.sv delete mode 100644 test/tb_apb_ro_regs.wave.do diff --git a/Bender.yml b/Bender.yml index 28c2f33..6109e62 100644 --- a/Bender.yml +++ b/Bender.yml @@ -20,9 +20,7 @@ sources: # Level 1 - src/apb_intf.sv # Level 2 - - src/apb_bus.sv - - src/apb_ro_regs.sv - - src/apb_rw_regs.sv + - src/apb_regs.sv - target: simulation files: @@ -30,8 +28,7 @@ sources: - target: test files: - - test/tb_apb_ro_regs.sv - - test/tb_apb_rw_regs.sv + - test/tb_apb_regs.sv - target: synth_test files: diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f4d8b..9cda48d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add clocked `APB_DV` interface for design verification. - Define macros for APB typedefs. - Define macros for assigning APB interfaces. -- Add read-only and read-write registers with APB interface. +- Add `apb_regs` read-write registers with APB interface with optional read only mapping. - Add basic test infrastructure for APB modules. - Add contribution guidelines. - Add RTL testbenches for modules. @@ -18,10 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `synth_bench`: add synthesis bench. ### Changed -- Rename `APB_BUS` interface to `APB`, change its parameters to constants, and remove `in` and `out` - modports. -- `apb_ro_regs`: Use of `addr_decode` module for indexing and change to structs. -- `apb_rw_regs`: Use of `addr_decode` module for indexing and change to structs. +- Rename `APB_BUS` interface to `APB`, change its parameters to constants, and remove `in` and `out` modports. ### Fixed diff --git a/README.md b/README.md index 35ccc10..e74e6b4 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ Maintainer: Andreas Kurth | [`apb/typedef`](include/apb/typedef.svh) | Macros which define the APB4 request/response structs | | [`apb/assign`](include/apb/typedef.svh) | Macros which assign/set/translates APB4 interfaces and structs | - ### Interfaces | Name | Description | @@ -27,14 +26,7 @@ Maintainer: Andreas Kurth | Name | Description | |------------------------------------------|-------------------------------------------------------------------| -| [`apb_ro_regs`](src/apb_ro_regs.sv) | Read-only registers | -| [`apb_rw_regs`](src/apb_rw_regs.sv) | Read and write registers | - -### Intermediary Modules - -| Name | Description | -|------------------------------------------|-------------------------------------------------------------------| -| [`apb_bus`](src/apb_bus.sv) | APB bus with single master and multiple slave interfaces | +| [`apb_regs`](src/apb_regs.sv) | Read and write registers, with optional read only mapping | ### Verification and Simulation diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index 63243de..7c9ccd8 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -24,5 +24,4 @@ call_vsim() { grep "Errors: 0," vsim.log } -call_vsim tb_apb_ro_regs -64 -t 1ns -coverage -lib rtl -voptargs="+acc +cover=bcesfx" -call_vsim tb_apb_rw_regs -64 -t 1ns -coverage -lib rtl -voptargs="+acc +cover=bcesfx" +call_vsim tb_apb_regs -64 -t 1ns -coverage -lib rtl -voptargs="+acc +cover=bcesfx" diff --git a/src/apb_regs.sv b/src/apb_regs.sv new file mode 100644 index 0000000..7b8b607 --- /dev/null +++ b/src/apb_regs.sv @@ -0,0 +1,260 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// APB Read-Write Registers +// Description: This module exposes a number of registers on an APB interface. +// It responds to not mapped accesses with a slave error. +// Some of the registers can be configured to be read only. +// Parameters: +// - `NoApbRegs`: Number of registers. +// - `ApbAddrWidth`: Address width of `req_i.paddr`, is used to generate internal address map. +// - `AddrOffset`: The address offset in bytes between the registers. Is asserted to be a least +// `32'd4`, the reason is to prevent overlap in the address map. +// Each register is mapped to a 4 byte wide address range. When this parameter +// is bigger than `32'd4` there will be holes in the address map and the module +// answers with `apb_pkg::RESP_SLVERR`. It is recommended that this value is a +// power of 2, to prevent data alignment issues on upstream buses. +// - `ApbDataWidth`: The data width of the APB4 bus, this value can be up to `32'd32` (bit). +// - `RegDataWidth`: The data width of the registers, this value has to be less or equal to +// `ApbDataWidth`. If it is less the register gets zero extended for reads and +// higher bits on writes get ignored. +// - `ReadOnly`: This flag can specify a read only register at the given register index of the +// array. When in the array the corresponding bit is set, the `reg_init_i` signal +// at given index can be read out. Writes are ignored if the flag is set. +// - `req_t`: APB4 request struct. See macro definition in `include/typedef.svh` +// - `resp_t`: APB4 response struct. See macro definition in `include/typedef.svh` +// +// Ports: +// +// - `pclk_i: Clock input signal (1-bit). +// - `preset_ni: Asynchronous active low reset signal (1-bit). +// - `req_i: APB4 request struct, bundles all APB4 signals from the master (req_t). +// - `resp_o: APB4 response struct, bundles all APB4 signals to the master (resp_t). +// - `base_addr_i: Base address of this module, from here the registers `0` are mapped, starting +// with index `0`. All subsequent register with higher indices have their bases +// mapped with reg_index * `AddrOffset` from this value (ApbAddrWidth-bit). +// - `reg_init_i: Initialization value for each register, when the register index is configured +// as `ReadOnly[reg_index] == 1'b1` this value is passed through directly to +// the APB4 bus (Array size `NoApbRegs` * RegDataWidth-bit). +// - `reg_q_o: The current value of the register. If the register at the array index is +// read only, the `reg_init_i` value is passed through to the respective index +// (Array size `NoApbRegs` * RegDataWidth-bit). +// +// This file also features the module `apb_regs_intf`. The difference is that instead of the +// request and response structs it uses an `APB.Salve` interface. The parameters have the same +// Function, however are defined in `ALL_CAPS`. + + +`include "common_cells/registers.svh" + +module apb_regs #( + parameter int unsigned NoApbRegs = 32'd0, + parameter int unsigned ApbAddrWidth = 32'd0, + parameter int unsigned AddrOffset = 32'd4, + parameter int unsigned ApbDataWidth = 32'd0, + parameter int unsigned RegDataWidth = 32'd0, + parameter bit [NoApbRegs-1:0] ReadOnly = 32'h0, + parameter type req_t = logic, + parameter type resp_t = logic, + // DEPENDENT PARAMETERS DO NOT OVERWRITE! + parameter type apb_addr_t = logic[ApbAddrWidth-1:0], + parameter type reg_data_t = logic[RegDataWidth-1:0] +) ( + // APB Interface + input logic pclk_i, + input logic preset_ni, + input req_t req_i, + output resp_t resp_o, + // Register Interface + input apb_addr_t base_addr_i, // base address of the read/write registers + input reg_data_t [NoApbRegs-1:0] reg_init_i, + output reg_data_t [NoApbRegs-1:0] reg_q_o +); + localparam int unsigned IdxWidth = (NoApbRegs > 32'd1) ? $clog2(NoApbRegs) : 32'd1; + typedef logic [IdxWidth-1:0] idx_t; + typedef logic [ApbAddrWidth-1:0] apb_data_t; + typedef struct packed { + int unsigned idx; + apb_addr_t start_addr; + apb_addr_t end_addr; + } rule_t; + + // signal declarations + rule_t [NoApbRegs-1:0] addr_map; + idx_t reg_idx; + logic decode_valid; + // register signals + reg_data_t [NoApbRegs-1:0] reg_d, reg_q; + logic [NoApbRegs-1:0] reg_update; + + // generate address map for the registers + for (genvar i = 0; i < NoApbRegs; i++) begin: gen_reg_addr_map + assign addr_map[i] = '{ + idx: unsigned'(i), + start_addr: base_addr_i + apb_addr_t'( i * AddrOffset), + end_addr: base_addr_i + apb_addr_t'((i+32'd1) * AddrOffset) + }; + end + + always_comb begin + // default assignments + reg_d = reg_q; + reg_update = '0; + resp_o = '{ + pready: req_i.psel & req_i.penable, + prdata: apb_data_t'(32'h0BAD_B10C), + pslverr: apb_pkg::RESP_OKAY + }; + if (req_i.psel) begin + if (!decode_valid) begin + // Error response on decode errors + resp_o.pslverr = apb_pkg::RESP_SLVERR; + end else begin + if (req_i.pwrite) begin + if (!ReadOnly[reg_idx]) begin + for (int unsigned i = 0; i < RegDataWidth; i++) begin + if (req_i.pstrb[i/8]) begin + reg_d[reg_idx][i] = req_i.pwdata[i]; + end + end + reg_update[reg_idx] = |req_i.pstrb; + end else begin + // this register is read only + resp_o.pslverr = apb_pkg::RESP_SLVERR; + end + end else begin + if (!ReadOnly[reg_idx]) begin + resp_o.prdata = apb_data_t'(reg_q[reg_idx]); + end else begin + // for read only register the directly connect the init signal + resp_o.prdata = apb_data_t'(reg_init_i[reg_idx]); + end + end + end + end + end + + // output assignment and registers + for (genvar i = 0; i < NoApbRegs; i++) begin : gen_rw_regs + assign reg_q_o[i] = ReadOnly[i] ? reg_init_i[i] : reg_q[i]; + `FFLARN(reg_q[i], reg_d[i], reg_update[i], reg_init_i[i], pclk_i, preset_ni) + end + + addr_decode #( + .NoIndices ( NoApbRegs ), + .NoRules ( NoApbRegs ), + .addr_t ( apb_addr_t ), + .rule_t ( rule_t ) + ) i_addr_decode ( + .addr_i ( req_i.paddr ), + .addr_map_i ( addr_map ), + .idx_o ( reg_idx ), + .dec_valid_o ( decode_valid ), + .dec_error_o ( /*not used*/ ), + .en_default_idx_i ( '0 ), + .default_idx_i ( '0 ) + ); + + // Validate parameters. + // pragma translate_off + `ifndef VERILATOR + initial begin: p_assertions + assert (NoApbRegs > 32'd0) + else $fatal(1, "The number of registers must be at least 1!"); + assert (ApbAddrWidth > 32'd2) + else $fatal(1, "ApbAddrWidth is not wide enough, has to be at least 3 bit wide!"); + assert (AddrOffset > 32'd3) + else $fatal(1, "AddrOffset has to be at least 4 and is recommended to be a power of 2!"); + assert ($bits(req_i.paddr) == ApbAddrWidth) + else $fatal(1, "AddrWidth does not match req_i.paddr!"); + assert (ApbDataWidth == $bits(resp_o.prdata)) + else $fatal(1, "ApbDataWidth has to be: ApbDataWidth == $bits(req_i.prdata)!"); + assert (ApbDataWidth > 32'd0 && ApbDataWidth <= 32'd32) + else $fatal(1, "ApbDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); + assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) + else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); + assert (RegDataWidth > 32'd0 && RegDataWidth <= 32'd32) + else $fatal(1, "RegDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); + assert (RegDataWidth <= $bits(resp_o.prdata)) + else $fatal(1, "RegDataWidth has to be: RegDataWidth <= $bits(req_i.prdata)!"); + assert (NoApbRegs == $bits(ReadOnly)) + else $fatal(1, "Each register need a `ReadOnly` flag!"); + end + `endif + // pragma translate_on +endmodule + +`include "apb/assign.svh" +`include "apb/typedef.svh" + +module apb_regs_intf #( + parameter int unsigned NO_APB_REGS = 32'd0, // number of read only registers + parameter int unsigned APB_ADDR_WIDTH = 32'd0, // address width of `paddr` + parameter int unsigned ADDR_OFFSET = 32'd4, // address offset in bytes + parameter int unsigned APB_DATA_WIDTH = 32'd0, // data width of the registers + parameter int unsigned REG_DATA_WIDTH = 32'd0, + parameter bit [NO_APB_REGS-1:0] READ_ONLY = 32'h0, + // DEPENDENT PARAMETERS DO NOT OVERWRITE! + parameter type apb_addr_t = logic[APB_ADDR_WIDTH-1:0], + parameter type reg_data_t = logic[REG_DATA_WIDTH-1:0] +) ( + // APB Interface + input logic pclk_i, + input logic preset_ni, + APB.Slave slv, + // Register Interface + input apb_addr_t base_addr_i, // base address of the read only registers + input reg_data_t [NO_APB_REGS-1:0] reg_init_i, // initalisation value for the registers + output reg_data_t [NO_APB_REGS-1:0] reg_q_o +); + localparam int unsigned APB_STRB_WIDTH = cf_math_pkg::ceil_div(APB_DATA_WIDTH, 8); + typedef logic [APB_DATA_WIDTH-1:0] apb_data_t; + typedef logic [APB_STRB_WIDTH-1:0] apb_strb_t; + + `APB_TYPEDEF_REQ_T(apb_req_t, apb_addr_t, apb_data_t, apb_strb_t) + `APB_TYPEDEF_RESP_T(apb_resp_t, apb_data_t) + + apb_req_t apb_req; + apb_resp_t apb_resp; + + `APB_ASSIGN_TO_REQ(apb_req, slv) + `APB_ASSIGN_FROM_RESP(slv, apb_resp ) + + apb_regs #( + .NoApbRegs ( NO_APB_REGS ), + .ApbAddrWidth ( APB_ADDR_WIDTH ), + .AddrOffset ( ADDR_OFFSET ), + .ApbDataWidth ( APB_DATA_WIDTH ), + .RegDataWidth ( REG_DATA_WIDTH ), + .ReadOnly ( READ_ONLY ), + .req_t ( apb_req_t ), + .resp_t ( apb_resp_t ) + ) i_apb_regs ( + .pclk_i, + .preset_ni, + .req_i ( apb_req ), + .resp_o ( apb_resp ), + .base_addr_i, + .reg_init_i, + .reg_q_o + ); + + // Validate parameters. + // pragma translate_off + `ifndef VERILATOR + initial begin: p_assertions + assert (APB_ADDR_WIDTH == $bits(slv.paddr)) + else $fatal(1, "APB_ADDR_WIDTH does not match slv interface!"); + assert (APB_DATA_WIDTH == $bits(slv.pwdata)) + else $fatal(1, "APB_DATA_WIDTH does not match slv interface!"); + end + `endif + // pragma translate_on +endmodule diff --git a/src/apb_ro_regs.sv b/src/apb_ro_regs.sv deleted file mode 100644 index 6fc5ff6..0000000 --- a/src/apb_ro_regs.sv +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -// Author: Wolfgang Roenninger - -// APB Read-Only Registers -// This module exposes a number of registers (provided on the `reg_i` input) read-only on an -// APB interface. It responds to reads that are out of range and writes with a slave error. -// The registers are byte addressed and aligned to 4 byte (32bit)! If `reg_data_t` width is less -// than the data width of the APB4 port, the register read response gets extended with `'0`. -module apb_ro_regs #( - parameter int unsigned NoApbRegs = 32'd0, // number of read only registers - parameter int unsigned ApbAddrWidth = 32'd0, // address width of `req_i.paddr` - parameter int unsigned ApbDataWidth = 32'd0, // data width of the `req_i.pwdata` & `resp_o.prdata` - parameter int unsigned RegDataWidth = 32'd0, // data width of the registers - parameter type req_t = logic, // APB4 request type - parameter type resp_t = logic, // APB4 response type - // DEPENDENT PARAMETERS DO NOT OVERWRITE! - parameter type apb_addr_t = logic[ApbAddrWidth-1:0], - parameter type reg_data_t = logic[RegDataWidth-1:0] -) ( - // APB Interface - input logic pclk_i, - input logic preset_ni, - input req_t req_i, - output resp_t resp_o, - // Register Interface - input apb_addr_t base_addr_i, // base address of the read only registers - input reg_data_t [NoApbRegs-1:0] reg_i -); - localparam int unsigned IdxWidth = (NoApbRegs > 32'd1) ? $clog2(NoApbRegs) : 32'd1; - typedef logic [IdxWidth-1:0] idx_t; - typedef logic [ApbAddrWidth-1:0] apb_data_t; - typedef struct packed { - int unsigned idx; - apb_addr_t start_addr; - apb_addr_t end_addr; - } rule_t; - - // signal declarations - rule_t [NoApbRegs-1:0] addr_map; - idx_t reg_idx; - logic decode_valid; - - // generate address map for the registers - for (genvar i = 0; i < NoApbRegs; i++) begin: gen_reg_addr_map - assign addr_map[i] = '{ - idx: unsigned'(i), - start_addr: base_addr_i + apb_addr_t'( i * 32'd4), - end_addr: base_addr_i + apb_addr_t'((i+32'd1) * 32'd4) - }; - end - // read control - always_comb begin - resp_o = '{ - pready: req_i.psel & req_i.penable, - prdata: apb_data_t'(32'h0BAD_B10C), - pslverr: apb_pkg::RESP_OKAY - }; - if (req_i.psel) begin - if (req_i.pwrite || !decode_valid) begin - // Error response on writes and decode errors - resp_o.pslverr = apb_pkg::RESP_SLVERR; - end else begin - resp_o.prdata = apb_data_t'(reg_i[reg_idx]); - end - end - end - - addr_decode #( - .NoIndices ( NoApbRegs ), - .NoRules ( NoApbRegs ), - .addr_t ( apb_addr_t ), - .rule_t ( rule_t ) - ) i_addr_decode ( - .addr_i ( req_i.paddr ), - .addr_map_i ( addr_map ), - .idx_o ( reg_idx ), - .dec_valid_o ( decode_valid ), - .dec_error_o ( /*not used*/ ), - .en_default_idx_i ( '0 ), - .default_idx_i ( '0 ) - ); - - // Validate parameters. - // pragma translate_off - `ifndef VERILATOR - initial begin: p_assertions - assert (NoApbRegs > 32'd0) - else $fatal(1, "The number of registers must be at least 1!"); - assert (ApbAddrWidth > 32'd2) - else $fatal(1, "ApbAddrWidth is not wide enough, has to be at least 3 bit wide!"); - assert ($bits(req_i.paddr) == ApbAddrWidth) - else $fatal(1, "AddrWidth does not match req_i.paddr!"); - assert (ApbDataWidth == $bits(resp_o.prdata)) - else $fatal(1, "ApbDataWidth has to be: ApbDataWidth == $bits(req_i.prdata)!"); - assert (ApbDataWidth > 32'd0 && ApbDataWidth <= 32'd32) - else $fatal(1, "ApbDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); - assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) - else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); - assert (RegDataWidth > 32'd0 && RegDataWidth <= 32'd32) - else $fatal(1, "RegDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); - assert (RegDataWidth <= $bits(resp_o.prdata)) - else $fatal(1, "RegDataWidth has to be: RegDataWidth <= $bits(req_i.prdata)!"); - end - `endif - // pragma translate_on -endmodule - -`include "apb/assign.svh" -`include "apb/typedef.svh" - -module apb_ro_regs_intf #( - parameter int unsigned NO_APB_REGS = 32'd0, // number of read only registers - parameter int unsigned APB_ADDR_WIDTH = 32'd0, // address width of `paddr` - parameter int unsigned APB_DATA_WIDTH = 32'd0, // data width of the registers - parameter int unsigned REG_DATA_WIDTH = 32'd0, - // DEPENDENT PARAMETERS DO NOT OVERWRITE! - parameter type apb_addr_t = logic[APB_ADDR_WIDTH-1:0], - parameter type reg_data_t = logic[REG_DATA_WIDTH-1:0] -) ( - // APB Interface - input logic pclk_i, - input logic preset_ni, - APB.Slave slv, - // Register Interface - input apb_addr_t base_addr_i, // base address of the read only registers - input reg_data_t [NO_APB_REGS-1:0] reg_i -); - localparam int unsigned APB_STRB_WIDTH = cf_math_pkg::ceil_div(APB_DATA_WIDTH, 8); - typedef logic [APB_DATA_WIDTH-1:0] apb_data_t; - typedef logic [APB_STRB_WIDTH-1:0] apb_strb_t; - - `APB_TYPEDEF_REQ_T ( apb_req_t, apb_addr_t, apb_data_t, apb_strb_t ) - `APB_TYPEDEF_RESP_T ( apb_resp_t, apb_data_t ) - - apb_req_t apb_req; - apb_resp_t apb_resp; - - `APB_ASSIGN_TO_REQ ( apb_req, slv ) - `APB_ASSIGN_FROM_RESP ( slv, apb_resp ) - - apb_ro_regs #( - .NoApbRegs ( NO_APB_REGS ), - .ApbAddrWidth ( APB_ADDR_WIDTH ), - .ApbDataWidth ( APB_DATA_WIDTH ), - .RegDataWidth ( REG_DATA_WIDTH ), - .req_t ( apb_req_t ), - .resp_t ( apb_resp_t ) - ) i_apb_ro_regs ( - .pclk_i, - .preset_ni, - .req_i ( apb_req ), - .resp_o ( apb_resp ), - .base_addr_i, - .reg_i - ); - - // Validate parameters. - // pragma translate_off - `ifndef VERILATOR - initial begin: p_assertions - assert (APB_ADDR_WIDTH == $bits(slv.paddr)) - else $fatal(1, "APB_ADDR_WIDTH does not match slv interface!"); - assert (APB_DATA_WIDTH == $bits(slv.pwdata)) - else $fatal(1, "APB_DATA_WIDTH does not match slv interface!"); - end - `endif - // pragma translate_on -endmodule diff --git a/src/apb_rw_regs.sv b/src/apb_rw_regs.sv deleted file mode 100644 index 011f183..0000000 --- a/src/apb_rw_regs.sv +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -// APB Read-Write Registers -// This module exposes a number of registers (provided on the `reg_i` input) writable on an -// APB interface. It responds to accesses that are out of range with a slave error. -`include "common_cells/registers.svh" - -module apb_rw_regs #( - parameter int unsigned NoApbRegs = 32'd0, // number of read only registers - parameter int unsigned ApbAddrWidth = 32'd0, // address width of `req_i.paddr` - parameter int unsigned ApbDataWidth = 32'd0, // data width of the `req_i.pwdata` & `resp_o.prdata` - parameter int unsigned RegDataWidth = 32'd0, // data width of the registers - parameter type req_t = logic, // APB4 request type - parameter type resp_t = logic, // APB4 response type - // DEPENDENT PARAMETERS DO NOT OVERWRITE! - parameter type apb_addr_t = logic[ApbAddrWidth-1:0], - parameter type reg_data_t = logic[RegDataWidth-1:0] -) ( - // APB Interface - input logic pclk_i, - input logic preset_ni, - input req_t req_i, - output resp_t resp_o, - // Register Interface - input apb_addr_t base_addr_i, // base address of the read/write registers - input reg_data_t [NoApbRegs-1:0] reg_init_i, - output reg_data_t [NoApbRegs-1:0] reg_q_o -); - localparam int unsigned IdxWidth = (NoApbRegs > 32'd1) ? $clog2(NoApbRegs) : 32'd1; - typedef logic [IdxWidth-1:0] idx_t; - typedef logic [ApbAddrWidth-1:0] apb_data_t; - typedef struct packed { - int unsigned idx; - apb_addr_t start_addr; - apb_addr_t end_addr; - } rule_t; - - // signal declarations - rule_t [NoApbRegs-1:0] addr_map; - idx_t reg_idx; - logic decode_valid; - // register signals - reg_data_t [NoApbRegs-1:0] reg_d, reg_q; - logic [NoApbRegs-1:0] reg_update; - - // generate address map for the registers - for (genvar i = 0; i < NoApbRegs; i++) begin: gen_reg_addr_map - assign addr_map[i] = '{ - idx: unsigned'(i), - start_addr: base_addr_i + apb_addr_t'( i * 32'd4), - end_addr: base_addr_i + apb_addr_t'((i+32'd1) * 32'd4) - }; - end - - always_comb begin - // default assignments - reg_d = reg_q; - reg_update = '0; - resp_o = '{ - pready: req_i.psel & req_i.penable, - prdata: apb_data_t'(32'h0BAD_B10C), - pslverr: apb_pkg::RESP_OKAY - }; - if (req_i.psel) begin - if (!decode_valid) begin - // Error response on decode errors - resp_o.pslverr = apb_pkg::RESP_SLVERR; - end else begin - if (req_i.pwrite) begin - for (int unsigned i = 0; i < RegDataWidth; i++) begin - if (req_i.pstrb) begin - reg_d[reg_idx][i] = req_i.pwdata[i]; - end - end - reg_update[reg_idx] = |req_i.pstrb; - end else begin - resp_o.prdata = apb_data_t'(reg_q[reg_idx]); - end - end - end - end - - // output assignment - assign reg_q_o = reg_q; - - for (genvar i = 0; i < NoApbRegs; i++) begin : gen_rw_regs - `FFLARN(reg_q[i], reg_d[i], reg_update[i], reg_init_i[i], pclk_i, preset_ni) - end - - addr_decode #( - .NoIndices ( NoApbRegs ), - .NoRules ( NoApbRegs ), - .addr_t ( apb_addr_t ), - .rule_t ( rule_t ) - ) i_addr_decode ( - .addr_i ( req_i.paddr ), - .addr_map_i ( addr_map ), - .idx_o ( reg_idx ), - .dec_valid_o ( decode_valid ), - .dec_error_o ( /*not used*/ ), - .en_default_idx_i ( '0 ), - .default_idx_i ( '0 ) - ); - - // Validate parameters. - // pragma translate_off - `ifndef VERILATOR - initial begin: p_assertions - assert (NoApbRegs > 32'd0) - else $fatal(1, "The number of registers must be at least 1!"); - assert (ApbAddrWidth > 32'd2) - else $fatal(1, "ApbAddrWidth is not wide enough, has to be at least 3 bit wide!"); - assert ($bits(req_i.paddr) == ApbAddrWidth) - else $fatal(1, "AddrWidth does not match req_i.paddr!"); - assert (ApbDataWidth == $bits(resp_o.prdata)) - else $fatal(1, "ApbDataWidth has to be: ApbDataWidth == $bits(req_i.prdata)!"); - assert (ApbDataWidth > 32'd0 && ApbDataWidth <= 32'd32) - else $fatal(1, "ApbDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); - assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) - else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); - assert (RegDataWidth > 32'd0 && RegDataWidth <= 32'd32) - else $fatal(1, "RegDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); - assert (RegDataWidth <= $bits(resp_o.prdata)) - else $fatal(1, "RegDataWidth has to be: RegDataWidth <= $bits(req_i.prdata)!"); - end - `endif - // pragma translate_on -endmodule - -`include "apb/assign.svh" -`include "apb/typedef.svh" - -module apb_rw_regs_intf #( - parameter int unsigned NO_APB_REGS = 32'd0, // number of read only registers - parameter int unsigned APB_ADDR_WIDTH = 32'd0, // address width of `paddr` - parameter int unsigned APB_DATA_WIDTH = 32'd0, // data width of the registers - parameter int unsigned REG_DATA_WIDTH = 32'd0, - // DEPENDENT PARAMETERS DO NOT OVERWRITE! - parameter type apb_addr_t = logic[APB_ADDR_WIDTH-1:0], - parameter type reg_data_t = logic[REG_DATA_WIDTH-1:0] -) ( - // APB Interface - input logic pclk_i, - input logic preset_ni, - APB.Slave slv, - // Register Interface - input apb_addr_t base_addr_i, // base address of the read only registers - input reg_data_t [NO_APB_REGS-1:0] reg_init_i, // initalisation value for the registers - output reg_data_t [NO_APB_REGS-1:0] reg_q_o -); - localparam int unsigned APB_STRB_WIDTH = cf_math_pkg::ceil_div(APB_DATA_WIDTH, 8); - typedef logic [APB_DATA_WIDTH-1:0] apb_data_t; - typedef logic [APB_STRB_WIDTH-1:0] apb_strb_t; - - `APB_TYPEDEF_REQ_T ( apb_req_t, apb_addr_t, apb_data_t, apb_strb_t ) - `APB_TYPEDEF_RESP_T ( apb_resp_t, apb_data_t ) - - apb_req_t apb_req; - apb_resp_t apb_resp; - - `APB_ASSIGN_TO_REQ ( apb_req, slv ) - `APB_ASSIGN_FROM_RESP ( slv, apb_resp ) - - apb_rw_regs #( - .NoApbRegs ( NO_APB_REGS ), - .ApbAddrWidth ( APB_ADDR_WIDTH ), - .ApbDataWidth ( APB_DATA_WIDTH ), - .RegDataWidth ( REG_DATA_WIDTH ), - .req_t ( apb_req_t ), - .resp_t ( apb_resp_t ) - ) i_apb_rw_regs ( - .pclk_i, - .preset_ni, - .req_i ( apb_req ), - .resp_o ( apb_resp ), - .base_addr_i, - .reg_init_i, - .reg_q_o - ); - - // Validate parameters. - // pragma translate_off - `ifndef VERILATOR - initial begin: p_assertions - assert (APB_ADDR_WIDTH == $bits(slv.paddr)) - else $fatal(1, "APB_ADDR_WIDTH does not match slv interface!"); - assert (APB_DATA_WIDTH == $bits(slv.pwdata)) - else $fatal(1, "APB_DATA_WIDTH does not match slv interface!"); - end - `endif - // pragma translate_on -endmodule diff --git a/test/synth_bench.sv b/test/synth_bench.sv index d81552e..9fc0454 100644 --- a/test/synth_bench.sv +++ b/test/synth_bench.sv @@ -16,86 +16,33 @@ module synth_bench ( input logic rst_ni ); - localparam int unsigned APB_ADDR_WIDTH[6] = {3, 7, 16, 27, 32}; - localparam int unsigned APB_DATA_WIDTH[6] = {1, 2, 16, 27, 32}; - localparam int unsigned REG_DATA_WIDTH[6] = {1, 4, 13, 27, 32}; + localparam int unsigned APB_ADDR_WIDTH[5] = {3, 7, 16, 27, 32}; + localparam int unsigned APB_DATA_WIDTH[5] = {1, 2, 16, 27, 32}; + localparam int unsigned REG_DATA_WIDTH[5] = {1, 4, 13, 27, 32}; - // APB RO REGS + // APB REGS for (genvar i = 0; i < 5; i++) begin : gen_ro_addr for (genvar j = 0; j < 5; j++) begin : gen_ro_data for (genvar k = 0; k < 5; k++) begin : gen_ro_width for (genvar l = 0; l < 4; l++) begin : gen_no_ro_regs localparam int unsigned NoApbRegs = 2**l; - localparam int unsigned RegDataWidth = (APB_DATA_WIDTH[k] > REG_DATA_WIDTH[k]) ? - APB_DATA_WIDTH[k] : REG_DATA_WIDTH[k]; - synth_apb_ro_regs #( + localparam int unsigned RegDataWidth = (APB_DATA_WIDTH[j] >= REG_DATA_WIDTH[k]) ? + REG_DATA_WIDTH[k] : APB_DATA_WIDTH[j]; + synth_apb_regs #( .NoApbRegs ( NoApbRegs ), .ApbAddrWidth( APB_ADDR_WIDTH[i] ), .ApbDataWidth( APB_DATA_WIDTH[j] ), .RegDataWidth( RegDataWidth ) - ) i_synth_ro_regs (.*); + ) i_synth_apb_regs (.*); end end end end - // APB RW REGS - for (genvar i = 0; i < 6; i++) begin : gen_rw_addr - for (genvar j = 0; j < 6; j++) begin : gen_rw_data - for (genvar k = 0; k < 4; k++) begin : gen_rw_width - for (genvar l = 0; l < 4; l++) begin : gen_no_rw_regs - localparam int unsigned NoApbRegs = 2**l; - localparam int unsigned RegDataWidth = (APB_DATA_WIDTH[k] > REG_DATA_WIDTH[k]) ? - APB_DATA_WIDTH[k] : REG_DATA_WIDTH[k]; - synth_apb_rw_regs #( - .NoApbRegs ( NoApbRegs ), - .ApbAddrWidth( APB_ADDR_WIDTH[i] ), - .ApbDataWidth( APB_DATA_WIDTH[j] ), - .RegDataWidth( RegDataWidth ) - ) i_synth_ro_regs (.*); - end - end - end - end -endmodule - - -module synth_apb_ro_regs #( - parameter int unsigned NoApbRegs = 32'd0, - parameter int unsigned ApbAddrWidth = 32'd0, - parameter int unsigned ApbDataWidth = 32'd0, - parameter int unsigned RegDataWidth = 32'd0 -) ( - input logic clk_i, - input logic rst_ni -); - typedef logic [ApbAddrWidth-1:0] apb_addr_t; - typedef logic [RegDataWidth-1:0] reg_data_t; - - APB #( - .ADDR_WIDTH ( ApbAddrWidth ), - .DATA_WIDTH ( ApbDataWidth ) - ) apb_slave(); - - apb_addr_t base_addr; - reg_data_t [NoApbRegs-1:0] register; - - apb_ro_regs_intf #( - .NO_APB_REGS ( NoApbRegs ), - .APB_ADDR_WIDTH ( ApbAddrWidth ), - .APB_DATA_WIDTH ( ApbDataWidth ), - .REG_DATA_WIDTH ( RegDataWidth ) - ) i_apb_ro_reg_intf ( - .pclk_i ( clk_i ), - .preset_ni ( rst_ni ), - .slv ( apb_slave ), - .base_addr_i ( base_addr ), - .reg_i ( register ) - ); endmodule -module synth_apb_rw_regs #( +module synth_apb_regs #( parameter int unsigned NoApbRegs = 32'd0, parameter int unsigned ApbAddrWidth = 32'd0, parameter int unsigned ApbDataWidth = 32'd0, @@ -104,6 +51,7 @@ module synth_apb_rw_regs #( input logic clk_i, input logic rst_ni ); + localparam logic [NoApbRegs-1:0] ReadOnly = {NoApbRegs{1'b0}}; typedef logic [ApbAddrWidth-1:0] apb_addr_t; typedef logic [RegDataWidth-1:0] reg_data_t; @@ -113,19 +61,21 @@ module synth_apb_rw_regs #( ) apb_slave(); apb_addr_t base_addr; - reg_data_t [NoApbRegs-1:0] register, reg_init; + reg_data_t [NoApbRegs-1:0] register, reg_q; - apb_rw_regs_intf #( + apb_regs_intf #( .NO_APB_REGS ( NoApbRegs ), .APB_ADDR_WIDTH ( ApbAddrWidth ), + .ADDR_OFFSET ( 32'd4 ), .APB_DATA_WIDTH ( ApbDataWidth ), - .REG_DATA_WIDTH ( RegDataWidth ) - ) i_apb_rw_reg_intf ( + .REG_DATA_WIDTH ( RegDataWidth ), + .READ_ONLY ( ReadOnly ) + ) i_apb_reg_intf ( .pclk_i ( clk_i ), .preset_ni ( rst_ni ), .slv ( apb_slave ), .base_addr_i ( base_addr ), - .reg_init_i ( reg_init ), - .reg_q_o ( register ) + .reg_init_i ( register ), + .reg_q_o ( reg_q ) ); endmodule diff --git a/test/tb_apb_rw_regs.sv b/test/tb_apb_regs.sv similarity index 80% rename from test/tb_apb_rw_regs.sv rename to test/tb_apb_regs.sv index ad6bc4d..f356f12 100644 --- a/test/tb_apb_rw_regs.sv +++ b/test/tb_apb_regs.sv @@ -17,19 +17,20 @@ `include "apb/assign.svh" -module tb_apb_rw_regs; - - localparam int unsigned NoApbRegs = 32'd342; +module tb_apb_regs; localparam int unsigned ApbAddrWidth = 32'd32; localparam int unsigned ApbDataWidth = 32'd27; localparam int unsigned ApbStrbWidth = cf_math_pkg::ceil_div(ApbDataWidth, 8); localparam int unsigned RegDataWidth = 32'd16; + localparam int unsigned NoApbRegs = 32'd342; + localparam logic [NoApbRegs-1:0] ReadOnly = 342'hFFF0; + localparam time CyclTime = 10ns; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - localparam int unsigned NoRandAccesses = 32'd10000; + localparam int unsigned NoRandAccesses = 32'd50000; typedef logic [ApbAddrWidth-1:0] apb_addr_t; typedef logic [ApbDataWidth-1:0] apb_data_t; @@ -40,6 +41,7 @@ module tb_apb_rw_regs; localparam apb_addr_t TestStartAddr = 32'h0002_FF00; localparam apb_addr_t TestEndAddr = 32'h0003_0F00; + logic clk; logic rst_n; logic done; @@ -84,9 +86,9 @@ module tb_apb_rw_regs; automatic reg_data_t init_val; // initialize reset values and golden model for (int unsigned i = 0; i < NoApbRegs; i++) begin - init_val = reg_data_t'($urandom()); - reg_init[i] = init_val; - reg_compare = init_val; + init_val = reg_data_t'($urandom()); + reg_init[i] = init_val; + reg_compare[i] = init_val; end done <= 1'b0; @@ -96,11 +98,25 @@ module tb_apb_rw_regs; repeat (10) @(posedge clk); // First test - apb_master.write( BaseAddr, apb_data_t'(32'd0000_0000), apb_strb_t'(4'hF), resp); + addr = apb_addr_t'(BaseAddr); + data = apb_data_t'(32'd0000_0000); + strb = apb_strb_t'(4'hF); + + apb_master.write( addr, data, strb, resp); $display("Write addr: %0h", addr); $display("Write data: %0h strb: %0h", data, strb); $display("Write resp: %0h", resp); - assert(resp == apb_pkg::RESP_SLVERR); + assert(resp == apb_pkg::RESP_OKAY); + if (resp == apb_pkg::RESP_OKAY) begin + // update golden model + $display("Update golden model"); + for (int unsigned j = 0; j < RegDataWidth; j++) begin + if (strb[j/8]) begin + $info("Write bit @%0h, bit: %0h", addr>>2, j); + reg_compare[(addr-BaseAddr)>>2][j] = data[j]; + end + end + end // Step 1 for (int unsigned i = TestStartAddr; i < TestEndAddr; i++) begin @@ -127,7 +143,7 @@ module tb_apb_rw_regs; // update golden model for (int unsigned j = 0; j < RegDataWidth; j++) begin if (strb[j/8]) begin - reg_compare[j] = data[j]; + reg_compare[(addr-BaseAddr)>>2][j] = data[j]; end end end @@ -137,6 +153,7 @@ module tb_apb_rw_regs; $display("Read data: %0h", data); $display("Read resp: %0h", resp); end + $display("Last Addr: %0h", last_addr); repeat ($urandom_range(0,5)) @(posedge clk); end @@ -210,23 +227,29 @@ module tb_apb_rw_regs; ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pstrb))); correct_rdata: assert property ( @(posedge clk) - (APB_TRANSFER and APB_RESP_OKAY and !apb_slave.pwrite) - |-> (apb_slave.prdata == apb_data_t'(reg_compare[apb_slave.paddr>>2]))) else + (apb_slave.penable && apb_slave.pready && + (apb_slave.pslverr == apb_pkg::RESP_OKAY) && !apb_slave.pwrite) + |-> (apb_slave.prdata == apb_data_t'(reg_compare[(apb_slave.paddr-BaseAddr)>>2]))) else $fatal(1, "Unexpected read response!"); correct_wdata: assert property ( @(posedge clk) - (APB_TRANSFER and APB_RESP_OKAY and apb_slave.pwrite) - |=> (reg_data[last_addr>>2] == reg_compare[last_addr>>2])) else - $fatal(1, "Unexpected write output!"); + ((apb_slave.penable && apb_slave.pready && + (apb_slave.pslverr == apb_pkg::RESP_OKAY) && apb_slave.pwrite) + |=> (reg_data[(last_addr-BaseAddr)>>2] == reg_compare[(last_addr-BaseAddr)>>2]))) else + $fatal(1, "Unexpected write output!: addr: %0h, output: %0h, expected: %0h", + (last_addr-BaseAddr)>>2, reg_data[(last_addr-BaseAddr)>>2], + reg_compare[(last_addr-BaseAddr)>>2] ); `endif // pragma translate_on // Dut - apb_rw_regs_intf #( + apb_regs_intf #( .NO_APB_REGS ( NoApbRegs ), + .ADDR_OFFSET ( 32'd4 ), .APB_ADDR_WIDTH ( ApbAddrWidth ), .APB_DATA_WIDTH ( ApbDataWidth ), - .REG_DATA_WIDTH ( RegDataWidth ) - ) i_apb_rw_regs_dut ( + .REG_DATA_WIDTH ( RegDataWidth ), + .READ_ONLY ( ReadOnly ) + ) i_apb_regs_dut ( .pclk_i ( clk ), .preset_ni ( rst_n ), .slv ( apb_slave ), diff --git a/test/tb_apb_rw_regs.wave.do b/test/tb_apb_regs.wave.do similarity index 56% rename from test/tb_apb_rw_regs.wave.do rename to test/tb_apb_regs.wave.do index 4bda443..36aaf73 100644 --- a/test/tb_apb_rw_regs.wave.do +++ b/test/tb_apb_regs.wave.do @@ -1,13 +1,14 @@ log -r * onerror {resume} quietly WaveActivateNextPane {} 0 -add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/pclk_i -add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/preset_ni -add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/base_addr_i -add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/reg_init_i -add wave -noupdate /tb_apb_rw_regs/i_apb_rw_regs_dut/reg_q_o -add wave -noupdate -expand /tb_apb_rw_regs/i_apb_rw_regs_dut/apb_req -add wave -noupdate -expand /tb_apb_rw_regs/i_apb_rw_regs_dut/apb_resp +add wave -noupdate /tb_apb_regs/i_apb_regs_dut/pclk_i +add wave -noupdate /tb_apb_regs/i_apb_regs_dut/preset_ni +add wave -noupdate /tb_apb_regs/i_apb_regs_dut/base_addr_i +add wave -noupdate -expand /tb_apb_regs/i_apb_regs_dut/apb_req +add wave -noupdate -expand /tb_apb_regs/i_apb_regs_dut/apb_resp +add wave -noupdate /tb_apb_regs/i_apb_regs_dut/base_addr_i +add wave -noupdate /tb_apb_regs/i_apb_regs_dut/reg_init_i +add wave -noupdate /tb_apb_regs/i_apb_regs_dut/reg_q_o TreeUpdate [SetDefaultTree] WaveRestoreCursors {{Cursor 1} {0 ns} 0} quietly wave cursor active 0 diff --git a/test/tb_apb_ro_regs.sv b/test/tb_apb_ro_regs.sv deleted file mode 100644 index 29c8f76..0000000 --- a/test/tb_apb_ro_regs.sv +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -// Author: Wolfgang Roenninger - -// Description: Testbench for `apb_ro_regs`. - -`include "apb/assign.svh" - -module tb_apb_ro_regs; - - localparam int unsigned NoApbRegs = 32'd342; - - localparam int unsigned ApbAddrWidth = 32'd32; - localparam int unsigned ApbDataWidth = 32'd27; - localparam int unsigned ApbStrbWidth = cf_math_pkg::ceil_div(ApbDataWidth, 8); - localparam int unsigned RegDataWidth = 32'd16; - - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - - typedef logic [ApbAddrWidth-1:0] apb_addr_t; - typedef logic [ApbDataWidth-1:0] apb_data_t; - typedef logic [ApbStrbWidth-1:0] apb_strb_t; - typedef logic [RegDataWidth-1:0] reg_data_t; - - localparam apb_addr_t BaseAddr = 32'h0003_0000; - localparam apb_addr_t TestStartAddr = 32'h0002_FF00; - localparam apb_addr_t TestEndAddr = 32'h0003_0F00; - - logic clk; - logic rst_n; - logic done; - reg_data_t [NoApbRegs-1:0] reg_data; - - - APB_DV #( - .ADDR_WIDTH ( ApbAddrWidth ), - .DATA_WIDTH ( ApbDataWidth ) - ) apb_slave_dv(clk); - APB #( - .ADDR_WIDTH ( ApbAddrWidth ), - .DATA_WIDTH ( ApbDataWidth ) - ) apb_slave(); - `APB_ASSIGN ( apb_slave, apb_slave_dv ) - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .CLK_PERIOD ( CyclTime ), - .RST_CLK_CYCLES( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - apb_test::apb_driver #( - .ADDR_WIDTH ( ApbAddrWidth ), - .DATA_WIDTH ( ApbDataWidth ), - .TA ( ApplTime ), - .TT ( TestTime ) - ) apb_master = new(apb_slave_dv); - - initial begin : proc_apb_master - automatic apb_addr_t addr; - automatic apb_data_t data; - automatic logic resp; - - done <= 1'b0; - @(posedge rst_n); - apb_master.reset_master(); - repeat (10) @(posedge clk); - apb_master.write( BaseAddr, apb_data_t'(32'd0000_0000), apb_strb_t'(4'hF), resp); - $display("Write addr: %0h", addr); - $display("Write data: %0h", data); - $display("Write resp: %0h", resp); - assert(resp == apb_pkg::RESP_SLVERR); - - for (int unsigned i = TestStartAddr; i < TestEndAddr; i++) begin - addr = apb_addr_t'(i); - apb_master.read(addr, data, resp); - $display("Read from addr: %0h", addr); - $display("Read data: %0h", data); - $display("Read resp: %0h", resp); - repeat ($urandom_range(0,5)) @(posedge clk); - end - done <= 1'b1; - end - - initial begin : proc_end_sim - @(posedge done); - repeat(10) @(posedge clk); - $stop(); - end - - initial begin : proc_set_reg_data - for (int unsigned i = 0; i < NoApbRegs; i++) begin - reg_data[i] = reg_data_t'($urandom()); - end - end - - // pragma translate_off - `ifndef VERILATOR - // Assertions to determine correct APB protocol sequencing - default disable iff (!rst_n); - // when psel is not asserted, the bus is in the idle state - sequence APB_IDLE; - !apb_slave.psel; - endsequence - - // when psel is set and penable is not, it is the setup state - sequence APB_SETUP; - apb_slave.psel && !apb_slave.penable; - endsequence - - // when psel and penable are set it is the access state - sequence APB_ACCESS; - apb_slave.psel && apb_slave.penable; - endsequence - - sequence APB_RESP_OKAY; - apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_OKAY); - endsequence - - sequence APB_RESP_SLVERR; - apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_SLVERR); - endsequence - - // APB Transfer is APB state going from setup to access - sequence APB_TRANSFER; - APB_SETUP ##1 APB_ACCESS; - endsequence - - apb_complete: assert property ( @(posedge clk) - (APB_SETUP |-> APB_TRANSFER)); - - apb_penable: assert property ( @(posedge clk) - (apb_slave.penable && apb_slave.psel && apb_slave.pready |=> (!apb_slave.penable))); - - control_stable: assert property ( @(posedge clk) - (APB_TRANSFER |-> $stable({apb_slave.pwrite, apb_slave.paddr}))); - - apb_valid: assert property ( @(posedge clk) - (APB_TRANSFER |-> ((!{apb_slave.pwrite, apb_slave.pstrb, apb_slave.paddr}) !== 1'bx))); - - write_stable: assert property ( @(posedge clk) - ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pwdata))); - - strb_stable: assert property ( @(posedge clk) - ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pstrb))); - - correct_data: assert property ( @(posedge clk) - (APB_TRANSFER and APB_RESP_OKAY and !apb_slave.pwrite) - |-> (apb_slave.prdata == reg_data[apb_slave.paddr>>2])) else - $fatal(1, "Unexpected read response!"); - `endif - // pragma translate_on - - - // Dut - apb_ro_regs_intf #( - .NO_APB_REGS ( NoApbRegs ), - .APB_ADDR_WIDTH ( ApbAddrWidth ), - .APB_DATA_WIDTH ( ApbDataWidth ), - .REG_DATA_WIDTH ( RegDataWidth ) - ) i_apb_ro_regs_dut ( - .pclk_i ( clk ), - .preset_ni ( rst_n ), - .slv ( apb_slave ), - .base_addr_i ( BaseAddr ), - .reg_i ( reg_data ) - ); - -endmodule diff --git a/test/tb_apb_ro_regs.wave.do b/test/tb_apb_ro_regs.wave.do deleted file mode 100644 index 8e5ce21..0000000 --- a/test/tb_apb_ro_regs.wave.do +++ /dev/null @@ -1,27 +0,0 @@ -log -r * -onerror {resume} -quietly WaveActivateNextPane {} 0 -add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/pclk_i -add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/preset_ni -add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/base_addr_i -add wave -noupdate /tb_apb_ro_regs/i_apb_ro_regs_dut/reg_i -add wave -noupdate -expand /tb_apb_ro_regs/i_apb_ro_regs_dut/apb_req -add wave -noupdate -expand /tb_apb_ro_regs/i_apb_ro_regs_dut/apb_resp -TreeUpdate [SetDefaultTree] -WaveRestoreCursors {{Cursor 1} {0 ns} 0} -quietly wave cursor active 0 -configure wave -namecolwidth 310 -configure wave -valuecolwidth 100 -configure wave -justifyvalue left -configure wave -signalnamewidth 0 -configure wave -snapdistance 10 -configure wave -datasetprefix 0 -configure wave -rowmargin 4 -configure wave -childrowmargin 2 -configure wave -gridoffset 0 -configure wave -gridperiod 1 -configure wave -griddelta 40 -configure wave -timeline 0 -configure wave -timelineunits ns -update -WaveRestoreZoom {0 ns} {20648 ns} From d1d5c190bcc1297216808505e19ca50a5a50a13b Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Fri, 13 Mar 2020 10:42:02 +0100 Subject: [PATCH 37/37] scripts/run_vsim.sh: Fix `-lib` to default library --- scripts/run_vsim.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index 7c9ccd8..5b0415f 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -24,4 +24,4 @@ call_vsim() { grep "Errors: 0," vsim.log } -call_vsim tb_apb_regs -64 -t 1ns -coverage -lib rtl -voptargs="+acc +cover=bcesfx" +call_vsim tb_apb_regs -64 -t 1ns -coverage -lib work -voptargs="+acc +cover=bcesfx"