mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 13:47:13 -04:00
Merge branch 'ariane_next' of github.com:pulp-platform/ariane into ariane_next
This commit is contained in:
commit
3635ddcd02
24 changed files with 1981 additions and 2083 deletions
|
@ -20,8 +20,10 @@
|
|||
// configuration in case Ariane is
|
||||
// instantiated in OpenPiton
|
||||
`ifdef PITON_ARIANE
|
||||
`ifndef AXI64_CACHE_PORTS
|
||||
`include "l15.tmp.h"
|
||||
`endif
|
||||
`endif
|
||||
|
||||
package ariane_pkg;
|
||||
|
||||
|
@ -284,7 +286,7 @@ package ariane_pkg;
|
|||
`define CONFIG_L1D_ASSOCIATIVITY 4
|
||||
`endif
|
||||
|
||||
`ifndef CONFIG_L1I_SIZE
|
||||
`ifndef CONFIG_L1D_SIZE
|
||||
`define CONFIG_L1D_SIZE 16*1024
|
||||
`endif
|
||||
|
||||
|
@ -298,7 +300,7 @@ package ariane_pkg;
|
|||
localparam int unsigned DCACHE_SET_ASSOC = `CONFIG_L1D_ASSOCIATIVITY;
|
||||
localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(`CONFIG_L1D_SIZE / DCACHE_SET_ASSOC);
|
||||
localparam int unsigned DCACHE_TAG_WIDTH = 56 - DCACHE_INDEX_WIDTH;
|
||||
`else
|
||||
`else
|
||||
// align to openpiton for the time being (this should be more configurable in the future)
|
||||
// I$
|
||||
localparam int unsigned ICACHE_INDEX_WIDTH = 12; // in bit
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
@ -20,334 +16,349 @@
|
|||
// configuration in case Ariane is
|
||||
// instantiated in OpenPiton
|
||||
`ifdef PITON_ARIANE
|
||||
`ifndef AXI64_CACHE_PORTS
|
||||
`include "l15.tmp.h"
|
||||
`include "define.tmp.h"
|
||||
`endif
|
||||
`endif
|
||||
|
||||
package serpent_cache_pkg;
|
||||
|
||||
// these parames need to coincide with the
|
||||
// L1.5 parameterization, do not change
|
||||
// these parames need to coincide with the
|
||||
// L1.5 parameterization, do not change
|
||||
`ifdef PITON_ARIANE
|
||||
localparam L15_SET_ASSOC = `CONFIG_L15_ASSOCIATIVITY;
|
||||
localparam L15_TID_WIDTH = `L15_THREADID_WIDTH;
|
||||
localparam L15_TLB_CSM_WIDTH = `TLB_CSM_WIDTH;
|
||||
`else
|
||||
localparam L15_SET_ASSOC = ariane_pkg::DCACHE_SET_ASSOC;// align with dcache for compatibility with the standard Ariane setup
|
||||
localparam L15_TID_WIDTH = 2;
|
||||
localparam L15_TLB_CSM_WIDTH = 33;
|
||||
|
||||
`ifndef CONFIG_L15_ASSOCIATIVITY
|
||||
`define CONFIG_L15_ASSOCIATIVITY 4
|
||||
`endif
|
||||
localparam L15_WAY_WIDTH = $clog2(L15_SET_ASSOC);
|
||||
localparam L1I_WAY_WIDTH = $clog2(ariane_pkg::ICACHE_SET_ASSOC);
|
||||
localparam L1D_WAY_WIDTH = $clog2(ariane_pkg::DCACHE_SET_ASSOC);
|
||||
|
||||
// FIFO depths of L15 adapter
|
||||
localparam ADAPTER_REQ_FIFO_DEPTH = 2;
|
||||
localparam ADAPTER_RTRN_FIFO_DEPTH = 2;
|
||||
`ifndef L15_THREADID_WIDTH
|
||||
`define L15_THREADID_WIDTH 1
|
||||
`endif
|
||||
|
||||
`ifndef TLB_CSM_WIDTH
|
||||
`define TLB_CSM_WIDTH 33
|
||||
`endif
|
||||
|
||||
localparam L15_SET_ASSOC = `CONFIG_L15_ASSOCIATIVITY;
|
||||
localparam L15_TID_WIDTH = `L15_THREADID_WIDTH;
|
||||
localparam L15_TLB_CSM_WIDTH = `TLB_CSM_WIDTH;
|
||||
`else
|
||||
localparam L15_SET_ASSOC = ariane_pkg::DCACHE_SET_ASSOC;// align with dcache for compatibility with the standard Ariane setup
|
||||
localparam L15_TID_WIDTH = 2;
|
||||
localparam L15_TLB_CSM_WIDTH = 33;
|
||||
`endif
|
||||
localparam L15_WAY_WIDTH = $clog2(L15_SET_ASSOC);
|
||||
localparam L1I_WAY_WIDTH = $clog2(ariane_pkg::ICACHE_SET_ASSOC);
|
||||
localparam L1D_WAY_WIDTH = $clog2(ariane_pkg::DCACHE_SET_ASSOC);
|
||||
|
||||
// FIFO depths of L15 adapter
|
||||
localparam ADAPTER_REQ_FIFO_DEPTH = 2;
|
||||
localparam ADAPTER_RTRN_FIFO_DEPTH = 2;
|
||||
|
||||
|
||||
// Calculated parameter
|
||||
localparam ICACHE_OFFSET_WIDTH = $clog2(ariane_pkg::ICACHE_LINE_WIDTH/8);
|
||||
localparam ICACHE_NUM_WORDS = 2**(ariane_pkg::ICACHE_INDEX_WIDTH-ICACHE_OFFSET_WIDTH);
|
||||
localparam ICACHE_CL_IDX_WIDTH = $clog2(ICACHE_NUM_WORDS);// excluding byte offset
|
||||
// Calculated parameter
|
||||
localparam ICACHE_OFFSET_WIDTH = $clog2(ariane_pkg::ICACHE_LINE_WIDTH/8);
|
||||
localparam ICACHE_NUM_WORDS = 2**(ariane_pkg::ICACHE_INDEX_WIDTH-ICACHE_OFFSET_WIDTH);
|
||||
localparam ICACHE_CL_IDX_WIDTH = $clog2(ICACHE_NUM_WORDS);// excluding byte offset
|
||||
|
||||
localparam DCACHE_OFFSET_WIDTH = $clog2(ariane_pkg::DCACHE_LINE_WIDTH/8);
|
||||
localparam DCACHE_NUM_WORDS = 2**(ariane_pkg::DCACHE_INDEX_WIDTH-DCACHE_OFFSET_WIDTH);
|
||||
localparam DCACHE_CL_IDX_WIDTH = $clog2(DCACHE_NUM_WORDS);// excluding byte offset
|
||||
localparam DCACHE_OFFSET_WIDTH = $clog2(ariane_pkg::DCACHE_LINE_WIDTH/8);
|
||||
localparam DCACHE_NUM_WORDS = 2**(ariane_pkg::DCACHE_INDEX_WIDTH-DCACHE_OFFSET_WIDTH);
|
||||
localparam DCACHE_CL_IDX_WIDTH = $clog2(DCACHE_NUM_WORDS);// excluding byte offset
|
||||
|
||||
localparam DCACHE_NUM_BANKS = ariane_pkg::DCACHE_LINE_WIDTH/64;
|
||||
localparam DCACHE_NUM_BANKS = ariane_pkg::DCACHE_LINE_WIDTH/64;
|
||||
|
||||
// write buffer parameterization
|
||||
localparam DCACHE_WBUF_DEPTH = 8;
|
||||
localparam DCACHE_MAX_TX = 2**L15_TID_WIDTH;
|
||||
localparam DCACHE_ID_WIDTH = $clog2(DCACHE_MAX_TX);
|
||||
// write buffer parameterization
|
||||
localparam DCACHE_WBUF_DEPTH = 8;
|
||||
localparam DCACHE_MAX_TX = 2**L15_TID_WIDTH;
|
||||
localparam DCACHE_ID_WIDTH = $clog2(DCACHE_MAX_TX);
|
||||
|
||||
|
||||
typedef struct packed {
|
||||
logic [ariane_pkg::DCACHE_INDEX_WIDTH+ariane_pkg::DCACHE_TAG_WIDTH-1:0] wtag;
|
||||
logic [63:0] data;
|
||||
logic [7:0] dirty; // byte is dirty
|
||||
logic [7:0] valid; // byte is valid
|
||||
logic [7:0] txblock; // byte is part of transaction in-flight
|
||||
logic checked; // if cache state of this word has been checked
|
||||
logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] hit_oh; // valid way in the cache
|
||||
} wbuffer_t;
|
||||
typedef struct packed {
|
||||
logic [ariane_pkg::DCACHE_INDEX_WIDTH+ariane_pkg::DCACHE_TAG_WIDTH-1:0] wtag;
|
||||
logic [63:0] data;
|
||||
logic [7:0] dirty; // byte is dirty
|
||||
logic [7:0] valid; // byte is valid
|
||||
logic [7:0] txblock; // byte is part of transaction in-flight
|
||||
logic checked; // if cache state of this word has been checked
|
||||
logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] hit_oh; // valid way in the cache
|
||||
} wbuffer_t;
|
||||
|
||||
// TX status registers are indexed with the transaction ID
|
||||
// they basically store which bytes from which buffer entry are part
|
||||
// of that transaction
|
||||
typedef struct packed {
|
||||
logic vld;
|
||||
logic [7:0] be;
|
||||
logic [$clog2(DCACHE_WBUF_DEPTH)-1:0] ptr;
|
||||
} tx_stat_t;
|
||||
// TX status registers are indexed with the transaction ID
|
||||
// they basically store which bytes from which buffer entry are part
|
||||
// of that transaction
|
||||
typedef struct packed {
|
||||
logic vld;
|
||||
logic [7:0] be;
|
||||
logic [$clog2(DCACHE_WBUF_DEPTH)-1:0] ptr;
|
||||
} tx_stat_t;
|
||||
|
||||
// local interfaces between caches and L15 adapter
|
||||
typedef enum logic [1:0] {
|
||||
DCACHE_STORE_REQ,
|
||||
DCACHE_LOAD_REQ,
|
||||
DCACHE_ATOMIC_REQ,
|
||||
DCACHE_INT_REQ } dcache_out_t;
|
||||
// local interfaces between caches and L15 adapter
|
||||
typedef enum logic [1:0] {
|
||||
DCACHE_STORE_REQ,
|
||||
DCACHE_LOAD_REQ,
|
||||
DCACHE_ATOMIC_REQ,
|
||||
DCACHE_INT_REQ
|
||||
} dcache_out_t;
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
DCACHE_INV_REQ, // no ack from the core required
|
||||
DCACHE_STORE_ACK,// note: this may contain an invalidation vector, too
|
||||
DCACHE_LOAD_ACK,
|
||||
DCACHE_ATOMIC_ACK,
|
||||
DCACHE_INT_ACK } dcache_in_t;
|
||||
typedef enum logic [2:0] {
|
||||
DCACHE_INV_REQ, // no ack from the core required
|
||||
DCACHE_STORE_ACK,// note: this may contain an invalidation vector, too
|
||||
DCACHE_LOAD_ACK,
|
||||
DCACHE_ATOMIC_ACK,
|
||||
DCACHE_INT_ACK
|
||||
} dcache_in_t;
|
||||
|
||||
typedef enum logic [0:0] {
|
||||
ICACHE_INV_REQ, // no ack from the core required
|
||||
ICACHE_IFILL_ACK} icache_in_t;
|
||||
typedef enum logic [0:0] {
|
||||
ICACHE_INV_REQ, // no ack from the core required
|
||||
ICACHE_IFILL_ACK
|
||||
} icache_in_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic vld; // invalidate only affected way
|
||||
logic all; // invalidate all ways
|
||||
logic [ariane_pkg::ICACHE_INDEX_WIDTH-1:0] idx; // physical address to invalidate
|
||||
logic [L15_WAY_WIDTH-1:0] way; // way to invalidate
|
||||
} cache_inval_t;
|
||||
typedef struct packed {
|
||||
logic vld; // invalidate only affected way
|
||||
logic all; // invalidate all ways
|
||||
logic [ariane_pkg::ICACHE_INDEX_WIDTH-1:0] idx; // physical address to invalidate
|
||||
logic [L15_WAY_WIDTH-1:0] way; // way to invalidate
|
||||
} cache_inval_t;
|
||||
|
||||
// icache interface
|
||||
typedef struct packed {
|
||||
logic [$clog2(ariane_pkg::ICACHE_SET_ASSOC)-1:0] way; // way to replace
|
||||
logic [63:0] paddr; // physical address
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
} icache_req_t;
|
||||
// icache interface
|
||||
typedef struct packed {
|
||||
logic [$clog2(ariane_pkg::ICACHE_SET_ASSOC)-1:0] way; // way to replace
|
||||
logic [63:0] paddr; // physical address
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
} icache_req_t;
|
||||
|
||||
typedef struct packed {
|
||||
icache_in_t rtype; // see definitions above
|
||||
logic [ariane_pkg::ICACHE_LINE_WIDTH-1:0] data; // full cache line width
|
||||
cache_inval_t inv; // invalidation vector
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
logic f4b; // fetch 4 bytes only (from I/O space)
|
||||
} icache_rtrn_t;
|
||||
typedef struct packed {
|
||||
icache_in_t rtype; // see definitions above
|
||||
logic [ariane_pkg::ICACHE_LINE_WIDTH-1:0] data; // full cache line width
|
||||
cache_inval_t inv; // invalidation vector
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
logic f4b; // fetch 4 bytes only (from I/O space)
|
||||
} icache_rtrn_t;
|
||||
|
||||
// dcache interface
|
||||
typedef struct packed {
|
||||
dcache_out_t rtype; // see definitions above
|
||||
logic [2:0] size; // transaction size: 000=Byte 001=2Byte; 010=4Byte; 011=8Byte; 111=Cache line (16/32Byte)
|
||||
logic [L1D_WAY_WIDTH-1:0] way; // way to replace
|
||||
logic [63:0] paddr; // physical address
|
||||
logic [63:0] data; // word width of processor (no block stores at the moment)
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
ariane_pkg::amo_t amo_op; // amo opcode
|
||||
} dcache_req_t;
|
||||
// dcache interface
|
||||
typedef struct packed {
|
||||
dcache_out_t rtype; // see definitions above
|
||||
logic [2:0] size; // transaction size: 000=Byte 001=2Byte; 010=4Byte; 011=8Byte; 111=Cache line (16/32Byte)
|
||||
logic [L1D_WAY_WIDTH-1:0] way; // way to replace
|
||||
logic [63:0] paddr; // physical address
|
||||
logic [63:0] data; // word width of processor (no block stores at the moment)
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
ariane_pkg::amo_t amo_op; // amo opcode
|
||||
} dcache_req_t;
|
||||
|
||||
typedef struct packed {
|
||||
dcache_in_t rtype; // see definitions above
|
||||
logic [ariane_pkg::DCACHE_LINE_WIDTH-1:0] data; // full cache line width
|
||||
cache_inval_t inv; // invalidation vector
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
} dcache_rtrn_t;
|
||||
typedef struct packed {
|
||||
dcache_in_t rtype; // see definitions above
|
||||
logic [ariane_pkg::DCACHE_LINE_WIDTH-1:0] data; // full cache line width
|
||||
cache_inval_t inv; // invalidation vector
|
||||
logic nc; // noncacheable
|
||||
logic [L15_TID_WIDTH-1:0] tid; // threadi id (used as transaction id in Ariane)
|
||||
} dcache_rtrn_t;
|
||||
|
||||
|
||||
// taken from iop.h in openpiton
|
||||
// to l1.5 (only marked subset is used)
|
||||
typedef enum logic [4:0] {
|
||||
L15_LOAD_RQ = 5'b00000, // load request
|
||||
L15_IMISS_RQ = 5'b10000, // instruction fill request
|
||||
L15_STORE_RQ = 5'b00001, // store request
|
||||
L15_ATOMIC_RQ = 5'b00110, // atomic op
|
||||
//L15_CAS1_RQ = 5'b00010, // compare and swap1 packet (OpenSparc atomics)
|
||||
//L15_CAS2_RQ = 5'b00011, // compare and swap2 packet (OpenSparc atomics)
|
||||
//L15_SWAP_RQ = 5'b00110, // swap packet (OpenSparc atomics)
|
||||
L15_STRLOAD_RQ = 5'b00100, // unused
|
||||
L15_STRST_RQ = 5'b00101, // unused
|
||||
L15_STQ_RQ = 5'b00111, // unused
|
||||
L15_INT_RQ = 5'b01001, // interrupt request
|
||||
L15_FWD_RQ = 5'b01101, // unused
|
||||
L15_FWD_RPY = 5'b01110, // unused
|
||||
L15_RSVD_RQ = 5'b11111 // unused
|
||||
} l15_reqtypes_t;
|
||||
// taken from iop.h in openpiton
|
||||
// to l1.5 (only marked subset is used)
|
||||
typedef enum logic [4:0] {
|
||||
L15_LOAD_RQ = 5'b00000, // load request
|
||||
L15_IMISS_RQ = 5'b10000, // instruction fill request
|
||||
L15_STORE_RQ = 5'b00001, // store request
|
||||
L15_ATOMIC_RQ = 5'b00110, // atomic op
|
||||
//L15_CAS1_RQ = 5'b00010, // compare and swap1 packet (OpenSparc atomics)
|
||||
//L15_CAS2_RQ = 5'b00011, // compare and swap2 packet (OpenSparc atomics)
|
||||
//L15_SWAP_RQ = 5'b00110, // swap packet (OpenSparc atomics)
|
||||
L15_STRLOAD_RQ = 5'b00100, // unused
|
||||
L15_STRST_RQ = 5'b00101, // unused
|
||||
L15_STQ_RQ = 5'b00111, // unused
|
||||
L15_INT_RQ = 5'b01001, // interrupt request
|
||||
L15_FWD_RQ = 5'b01101, // unused
|
||||
L15_FWD_RPY = 5'b01110, // unused
|
||||
L15_RSVD_RQ = 5'b11111 // unused
|
||||
} l15_reqtypes_t;
|
||||
|
||||
// from l1.5 (only marked subset is used)
|
||||
typedef enum logic [3:0] {
|
||||
L15_LOAD_RET = 4'b0000, // load packet
|
||||
// L15_INV_RET = 4'b0011, // invalidate packet, not unique...
|
||||
L15_ST_ACK = 4'b0100, // store ack packet
|
||||
//L15_AT_ACK = 4'b0011, // unused, not unique...
|
||||
L15_INT_RET = 4'b0111, // interrupt packet
|
||||
L15_TEST_RET = 4'b0101, // unused
|
||||
L15_FP_RET = 4'b1000, // unused
|
||||
L15_IFILL_RET = 4'b0001, // instruction fill packet
|
||||
L15_EVICT_REQ = 4'b0011, // eviction request
|
||||
L15_ERR_RET = 4'b1100, // unused
|
||||
L15_STRLOAD_RET = 4'b0010, // unused
|
||||
L15_STRST_ACK = 4'b0110, // unused
|
||||
L15_FWD_RQ_RET = 4'b1010, // unused
|
||||
L15_FWD_RPY_RET = 4'b1011, // unused
|
||||
L15_RSVD_RET = 4'b1111, // unused
|
||||
L15_CPX_RESTYPE_ATOMIC_RES = 4'b1110 // custom type for atomic responses
|
||||
} l15_rtrntypes_t;
|
||||
// from l1.5 (only marked subset is used)
|
||||
typedef enum logic [3:0] {
|
||||
L15_LOAD_RET = 4'b0000, // load packet
|
||||
// L15_INV_RET = 4'b0011, // invalidate packet, not unique...
|
||||
L15_ST_ACK = 4'b0100, // store ack packet
|
||||
//L15_AT_ACK = 4'b0011, // unused, not unique...
|
||||
L15_INT_RET = 4'b0111, // interrupt packet
|
||||
L15_TEST_RET = 4'b0101, // unused
|
||||
L15_FP_RET = 4'b1000, // unused
|
||||
L15_IFILL_RET = 4'b0001, // instruction fill packet
|
||||
L15_EVICT_REQ = 4'b0011, // eviction request
|
||||
L15_ERR_RET = 4'b1100, // unused
|
||||
L15_STRLOAD_RET = 4'b0010, // unused
|
||||
L15_STRST_ACK = 4'b0110, // unused
|
||||
L15_FWD_RQ_RET = 4'b1010, // unused
|
||||
L15_FWD_RPY_RET = 4'b1011, // unused
|
||||
L15_RSVD_RET = 4'b1111, // unused
|
||||
L15_CPX_RESTYPE_ATOMIC_RES = 4'b1110 // custom type for atomic responses
|
||||
} l15_rtrntypes_t;
|
||||
|
||||
|
||||
typedef struct packed {
|
||||
logic l15_val; // valid signal, asserted with request
|
||||
logic l15_req_ack; // ack for response
|
||||
l15_reqtypes_t l15_rqtype; // see below for encoding
|
||||
logic l15_nc; // non-cacheable bit
|
||||
logic [2:0] l15_size; // transaction size: 000=Byte 001=2Byte; 010=4Byte; 011=8Byte; 111=Cache line (16/32Byte)
|
||||
logic [L15_TID_WIDTH-1:0] l15_threadid; // currently 0 or 1
|
||||
logic l15_prefetch; // unused in openpiton
|
||||
logic l15_invalidate_cacheline; // unused by Ariane as L1 has no ECC at the moment
|
||||
logic l15_blockstore; // unused in openpiton
|
||||
logic l15_blockinitstore; // unused in openpiton
|
||||
logic [L15_WAY_WIDTH-1:0] l15_l1rplway; // way to replace
|
||||
logic [39:0] l15_address; // physical address
|
||||
logic [63:0] l15_data; // word to write
|
||||
logic [63:0] l15_data_next_entry; // unused in Ariane (only used for CAS atomic requests)
|
||||
logic [L15_TLB_CSM_WIDTH-1:0] l15_csm_data; // unused in Ariane
|
||||
logic [3:0] l15_amo_op; // atomic operation type
|
||||
} l15_req_t;
|
||||
typedef struct packed {
|
||||
logic l15_val; // valid signal, asserted with request
|
||||
logic l15_req_ack; // ack for response
|
||||
l15_reqtypes_t l15_rqtype; // see below for encoding
|
||||
logic l15_nc; // non-cacheable bit
|
||||
logic [2:0] l15_size; // transaction size: 000=Byte 001=2Byte; 010=4Byte; 011=8Byte; 111=Cache line (16/32Byte)
|
||||
logic [L15_TID_WIDTH-1:0] l15_threadid; // currently 0 or 1
|
||||
logic l15_prefetch; // unused in openpiton
|
||||
logic l15_invalidate_cacheline; // unused by Ariane as L1 has no ECC at the moment
|
||||
logic l15_blockstore; // unused in openpiton
|
||||
logic l15_blockinitstore; // unused in openpiton
|
||||
logic [L15_WAY_WIDTH-1:0] l15_l1rplway; // way to replace
|
||||
logic [39:0] l15_address; // physical address
|
||||
logic [63:0] l15_data; // word to write
|
||||
logic [63:0] l15_data_next_entry; // unused in Ariane (only used for CAS atomic requests)
|
||||
logic [L15_TLB_CSM_WIDTH-1:0] l15_csm_data; // unused in Ariane
|
||||
logic [3:0] l15_amo_op; // atomic operation type
|
||||
} l15_req_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic l15_ack; // ack for request struct
|
||||
logic l15_header_ack; // ack for request struct
|
||||
logic l15_val; // valid signal for return struct
|
||||
l15_rtrntypes_t l15_returntype; // see below for encoding
|
||||
logic l15_l2miss; // unused in Ariane
|
||||
logic [1:0] l15_error; // unused in openpiton
|
||||
logic l15_noncacheable; // non-cacheable bit
|
||||
logic l15_atomic; // asserted in load return and store ack packets of atomic tx
|
||||
logic [L15_TID_WIDTH-1:0] l15_threadid; // used as transaction ID
|
||||
logic l15_prefetch; // unused in openpiton
|
||||
logic l15_f4b; // 4byte instruction fill from I/O space (nc).
|
||||
logic [63:0] l15_data_0; // used for both caches
|
||||
logic [63:0] l15_data_1; // used for both caches
|
||||
logic [63:0] l15_data_2; // currently only used for I$
|
||||
logic [63:0] l15_data_3; // currently only used for I$
|
||||
logic l15_inval_icache_all_way; // invalidate all ways
|
||||
logic l15_inval_dcache_all_way; // unused in openpiton
|
||||
logic [15:4] l15_inval_address_15_4; // invalidate selected cacheline
|
||||
logic l15_cross_invalidate; // unused in openpiton
|
||||
logic [L15_WAY_WIDTH-1:0] l15_cross_invalidate_way; // unused in openpiton
|
||||
logic l15_inval_dcache_inval; // invalidate selected cacheline and way
|
||||
logic l15_inval_icache_inval; // unused in openpiton
|
||||
logic [L15_WAY_WIDTH-1:0] l15_inval_way; // way to invalidate
|
||||
logic l15_blockinitstore; // unused in openpiton
|
||||
} l15_rtrn_t;
|
||||
typedef struct packed {
|
||||
logic l15_ack; // ack for request struct
|
||||
logic l15_header_ack; // ack for request struct
|
||||
logic l15_val; // valid signal for return struct
|
||||
l15_rtrntypes_t l15_returntype; // see below for encoding
|
||||
logic l15_l2miss; // unused in Ariane
|
||||
logic [1:0] l15_error; // unused in openpiton
|
||||
logic l15_noncacheable; // non-cacheable bit
|
||||
logic l15_atomic; // asserted in load return and store ack packets of atomic tx
|
||||
logic [L15_TID_WIDTH-1:0] l15_threadid; // used as transaction ID
|
||||
logic l15_prefetch; // unused in openpiton
|
||||
logic l15_f4b; // 4byte instruction fill from I/O space (nc).
|
||||
logic [63:0] l15_data_0; // used for both caches
|
||||
logic [63:0] l15_data_1; // used for both caches
|
||||
logic [63:0] l15_data_2; // currently only used for I$
|
||||
logic [63:0] l15_data_3; // currently only used for I$
|
||||
logic l15_inval_icache_all_way; // invalidate all ways
|
||||
logic l15_inval_dcache_all_way; // unused in openpiton
|
||||
logic [15:4] l15_inval_address_15_4; // invalidate selected cacheline
|
||||
logic l15_cross_invalidate; // unused in openpiton
|
||||
logic [L15_WAY_WIDTH-1:0] l15_cross_invalidate_way; // unused in openpiton
|
||||
logic l15_inval_dcache_inval; // invalidate selected cacheline and way
|
||||
logic l15_inval_icache_inval; // unused in openpiton
|
||||
logic [L15_WAY_WIDTH-1:0] l15_inval_way; // way to invalidate
|
||||
logic l15_blockinitstore; // unused in openpiton
|
||||
} l15_rtrn_t;
|
||||
|
||||
// swap endianess in a 64bit word
|
||||
function automatic logic[63:0] swendian64(input logic[63:0] in);
|
||||
automatic logic[63:0] out;
|
||||
for(int k=0; k<64;k+=8)begin
|
||||
out[k +: 8] = in[63-k -: 8];
|
||||
end
|
||||
return out;
|
||||
endfunction
|
||||
// swap endianess in a 64bit word
|
||||
function automatic logic[63:0] swendian64(input logic[63:0] in);
|
||||
automatic logic[63:0] out;
|
||||
for(int k=0; k<64;k+=8)begin
|
||||
out[k +: 8] = in[63-k -: 8];
|
||||
end
|
||||
return out;
|
||||
endfunction
|
||||
|
||||
function automatic logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] icache_way_bin2oh (
|
||||
input logic [$clog2(ariane_pkg::ICACHE_SET_ASSOC)-1:0] in
|
||||
);
|
||||
logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] out;
|
||||
out = '0;
|
||||
out[in] = 1'b1;
|
||||
return out;
|
||||
endfunction
|
||||
function automatic logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] icache_way_bin2oh (
|
||||
input logic [$clog2(ariane_pkg::ICACHE_SET_ASSOC)-1:0] in
|
||||
);
|
||||
logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] out;
|
||||
out = '0;
|
||||
out[in] = 1'b1;
|
||||
return out;
|
||||
endfunction
|
||||
|
||||
function automatic logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] dcache_way_bin2oh (
|
||||
input logic [$clog2(ariane_pkg::DCACHE_SET_ASSOC)-1:0] in
|
||||
);
|
||||
logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] out;
|
||||
out = '0;
|
||||
out[in] = 1'b1;
|
||||
return out;
|
||||
endfunction
|
||||
function automatic logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] dcache_way_bin2oh (
|
||||
input logic [$clog2(ariane_pkg::DCACHE_SET_ASSOC)-1:0] in
|
||||
);
|
||||
logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] out;
|
||||
out = '0;
|
||||
out[in] = 1'b1;
|
||||
return out;
|
||||
endfunction
|
||||
|
||||
function automatic logic [DCACHE_NUM_BANKS-1:0] dcache_cl_bin2oh (
|
||||
input logic [$clog2(DCACHE_NUM_BANKS)-1:0] in
|
||||
);
|
||||
logic [DCACHE_NUM_BANKS-1:0] out;
|
||||
out = '0;
|
||||
out[in] = 1'b1;
|
||||
return out;
|
||||
endfunction
|
||||
function automatic logic [DCACHE_NUM_BANKS-1:0] dcache_cl_bin2oh (
|
||||
input logic [$clog2(DCACHE_NUM_BANKS)-1:0] in
|
||||
);
|
||||
logic [DCACHE_NUM_BANKS-1:0] out;
|
||||
out = '0;
|
||||
out[in] = 1'b1;
|
||||
return out;
|
||||
endfunction
|
||||
|
||||
|
||||
function automatic logic [5:0] popcnt64 (
|
||||
input logic [63:0] in
|
||||
);
|
||||
logic [5:0] cnt= 0;
|
||||
foreach (in[k]) begin
|
||||
cnt += in[k];
|
||||
end
|
||||
return cnt;
|
||||
endfunction : popcnt64
|
||||
function automatic logic [5:0] popcnt64 (
|
||||
input logic [63:0] in
|
||||
);
|
||||
logic [5:0] cnt= 0;
|
||||
foreach (in[k]) begin
|
||||
cnt += in[k];
|
||||
end
|
||||
return cnt;
|
||||
endfunction : popcnt64
|
||||
|
||||
function automatic logic [7:0] toByteEnable8(
|
||||
input logic [2:0] offset,
|
||||
input logic [1:0] size
|
||||
);
|
||||
logic [7:0] be;
|
||||
be = '0;
|
||||
unique case(size)
|
||||
2'b00: be[offset] = '1; // byte
|
||||
2'b01: be[offset +:2 ] = '1; // hword
|
||||
2'b10: be[offset +:4 ] = '1; // word
|
||||
default: be = '1; // dword
|
||||
endcase // size
|
||||
return be;
|
||||
endfunction : toByteEnable8
|
||||
|
||||
// openpiton requires the data to be replicated in case of smaller sizes than dwords
|
||||
function automatic logic [63:0] repData64(
|
||||
input logic [63:0] data,
|
||||
input logic [2:0] offset,
|
||||
input logic [1:0] size
|
||||
);
|
||||
logic [63:0] out;
|
||||
unique case(size)
|
||||
2'b00: for(int k=0; k<8; k++) out[k*8 +: 8] = data[offset*8 +: 8]; // byte
|
||||
2'b01: for(int k=0; k<4; k++) out[k*16 +: 16] = data[offset*8 +: 16]; // hword
|
||||
2'b10: for(int k=0; k<2; k++) out[k*32 +: 32] = data[offset*8 +: 32]; // word
|
||||
default: out = data; // dword
|
||||
endcase // size
|
||||
return out;
|
||||
endfunction : repData64
|
||||
|
||||
// note: this is openpiton specific. cannot transmit unaligned words.
|
||||
// hence we default to individual bytes in that case, and they have to be transmitted
|
||||
// one after the other
|
||||
function automatic logic [1:0] toSize64(
|
||||
input logic [7:0] be
|
||||
);
|
||||
logic [1:0] size;
|
||||
unique case(be)
|
||||
8'b1111_1111: size = 2'b11; // dword
|
||||
8'b0000_1111, 8'b1111_0000: size = 2'b10; // word
|
||||
8'b1100_0000, 8'b0011_0000, 8'b0000_1100, 8'b0000_0011: size = 2'b01; // hword
|
||||
default: size = 2'b00; // individual bytes
|
||||
endcase // be
|
||||
return size;
|
||||
endfunction : toSize64
|
||||
|
||||
// align the physical address to the specified size:
|
||||
// 000: bytes
|
||||
// 001: hword
|
||||
// 010: word
|
||||
// 011: dword
|
||||
// 111: DCACHE line
|
||||
function automatic logic [63:0] paddrSizeAlign(
|
||||
input logic [63:0] paddr,
|
||||
input logic [2:0] size
|
||||
);
|
||||
logic [63:0] out;
|
||||
out = paddr;
|
||||
unique case (size)
|
||||
3'b001: out[0:0] = '0;
|
||||
3'b010: out[1:0] = '0;
|
||||
3'b011: out[2:0] = '0;
|
||||
3'b111: out[DCACHE_OFFSET_WIDTH-1:0] = '0;
|
||||
default: ;
|
||||
endcase
|
||||
return out;
|
||||
endfunction : paddrSizeAlign
|
||||
function automatic logic [7:0] toByteEnable8(
|
||||
input logic [2:0] offset,
|
||||
input logic [1:0] size
|
||||
);
|
||||
logic [7:0] be;
|
||||
be = '0;
|
||||
unique case(size)
|
||||
2'b00: be[offset] = '1; // byte
|
||||
2'b01: be[offset +:2 ] = '1; // hword
|
||||
2'b10: be[offset +:4 ] = '1; // word
|
||||
default: be = '1; // dword
|
||||
endcase // size
|
||||
return be;
|
||||
endfunction : toByteEnable8
|
||||
|
||||
// openpiton requires the data to be replicated in case of smaller sizes than dwords
|
||||
function automatic logic [63:0] repData64(
|
||||
input logic [63:0] data,
|
||||
input logic [2:0] offset,
|
||||
input logic [1:0] size
|
||||
);
|
||||
logic [63:0] out;
|
||||
unique case(size)
|
||||
2'b00: for(int k=0; k<8; k++) out[k*8 +: 8] = data[offset*8 +: 8]; // byte
|
||||
2'b01: for(int k=0; k<4; k++) out[k*16 +: 16] = data[offset*8 +: 16]; // hword
|
||||
2'b10: for(int k=0; k<2; k++) out[k*32 +: 32] = data[offset*8 +: 32]; // word
|
||||
default: out = data; // dword
|
||||
endcase // size
|
||||
return out;
|
||||
endfunction : repData64
|
||||
|
||||
// note: this is openpiton specific. cannot transmit unaligned words.
|
||||
// hence we default to individual bytes in that case, and they have to be transmitted
|
||||
// one after the other
|
||||
function automatic logic [1:0] toSize64(
|
||||
input logic [7:0] be
|
||||
);
|
||||
logic [1:0] size;
|
||||
unique case(be)
|
||||
8'b1111_1111: size = 2'b11; // dword
|
||||
8'b0000_1111, 8'b1111_0000: size = 2'b10; // word
|
||||
8'b1100_0000, 8'b0011_0000, 8'b0000_1100, 8'b0000_0011: size = 2'b01; // hword
|
||||
default: size = 2'b00; // individual bytes
|
||||
endcase // be
|
||||
return size;
|
||||
endfunction : toSize64
|
||||
|
||||
// align the physical address to the specified size:
|
||||
// 000: bytes
|
||||
// 001: hword
|
||||
// 010: word
|
||||
// 011: dword
|
||||
// 111: DCACHE line
|
||||
function automatic logic [63:0] paddrSizeAlign(
|
||||
input logic [63:0] paddr,
|
||||
input logic [2:0] size
|
||||
);
|
||||
logic [63:0] out;
|
||||
out = paddr;
|
||||
unique case (size)
|
||||
3'b001: out[0:0] = '0;
|
||||
3'b010: out[1:0] = '0;
|
||||
3'b011: out[2:0] = '0;
|
||||
3'b111: out[DCACHE_OFFSET_WIDTH-1:0] = '0;
|
||||
default: ;
|
||||
endcase
|
||||
return out;
|
||||
endfunction : paddrSizeAlign
|
||||
|
||||
endpackage : serpent_cache_pkg
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
//
|
||||
// Author: Michael Schaffner, ETH Zurich
|
||||
// Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 19.03.2017
|
||||
// Description: Ariane Top-level wrapper to break out SV structs to logic vectors.
|
||||
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
// 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.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
//
|
||||
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
// Description: Ariane cache subsystem that is compatible with the OpenPiton
|
||||
// coherent memory system.
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
// 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.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
//
|
||||
// Author: Michael Schaffner (schaffner@iis.ee.ethz.ch), ETH Zurich
|
||||
// Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 08.08.2018
|
||||
// Description: adapter module to connect the L1D$ and L1I$ to the native
|
||||
// interface of the OpenPiton L1.5 cache.
|
||||
|
|
365
src/serdiv.sv
365
src/serdiv.sv
|
@ -8,8 +8,8 @@
|
|||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
//
|
||||
// Author: Michael Schaffner (schaffner@iis.ee.ethz.ch), ETH Zurich
|
||||
// Andreas Traber (traber@iis.ee.ethz.ch), ETH Zurich
|
||||
// Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Andreas Traber <traber@iis.ee.ethz.ch>, ETH Zurich
|
||||
//
|
||||
// Date: 18.10.2018
|
||||
// Description: simple 64bit serial divider
|
||||
|
@ -17,61 +17,61 @@
|
|||
import ariane_pkg::*;
|
||||
|
||||
module serdiv #(
|
||||
parameter WIDTH = 64
|
||||
)(
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
// input IF
|
||||
input logic [TRANS_ID_BITS-1:0] id_i,
|
||||
input logic [WIDTH-1:0] op_a_i,
|
||||
input logic [WIDTH-1:0] op_b_i,
|
||||
input logic [1:0] opcode_i, // 0: udiv, 2: urem, 1: div, 3: rem
|
||||
// handshake
|
||||
input logic in_vld_i, // there is a cycle delay from in_rdy_o->in_vld_i, see issue_read_operands.sv stage
|
||||
output logic in_rdy_o,
|
||||
input logic flush_i,
|
||||
// output IF
|
||||
output logic out_vld_o,
|
||||
input logic out_rdy_i,
|
||||
output logic [TRANS_ID_BITS-1:0] id_o,
|
||||
output logic [WIDTH-1:0] res_o
|
||||
parameter WIDTH = 64
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
// input IF
|
||||
input logic [TRANS_ID_BITS-1:0] id_i,
|
||||
input logic [WIDTH-1:0] op_a_i,
|
||||
input logic [WIDTH-1:0] op_b_i,
|
||||
input logic [1:0] opcode_i, // 0: udiv, 2: urem, 1: div, 3: rem
|
||||
// handshake
|
||||
input logic in_vld_i, // there is a cycle delay from in_rdy_o->in_vld_i, see issue_read_operands.sv stage
|
||||
output logic in_rdy_o,
|
||||
input logic flush_i,
|
||||
// output IF
|
||||
output logic out_vld_o,
|
||||
input logic out_rdy_i,
|
||||
output logic [TRANS_ID_BITS-1:0] id_o,
|
||||
output logic [WIDTH-1:0] res_o
|
||||
);
|
||||
|
||||
/////////////////////////////////////
|
||||
// signal declarations
|
||||
/////////////////////////////////////
|
||||
|
||||
enum logic [1:0] {IDLE, DIVIDE, FINISH} state_d, state_q;
|
||||
enum logic [1:0] {IDLE, DIVIDE, FINISH} state_d, state_q;
|
||||
|
||||
logic [WIDTH-1:0] res_q, res_d;
|
||||
logic [WIDTH-1:0] op_a_q, op_a_d;
|
||||
logic [WIDTH-1:0] op_b_q, op_b_d;
|
||||
logic op_a_sign, op_b_sign;
|
||||
logic op_b_zero, op_b_zero_q, op_b_zero_d;
|
||||
logic [WIDTH-1:0] res_q, res_d;
|
||||
logic [WIDTH-1:0] op_a_q, op_a_d;
|
||||
logic [WIDTH-1:0] op_b_q, op_b_d;
|
||||
logic op_a_sign, op_b_sign;
|
||||
logic op_b_zero, op_b_zero_q, op_b_zero_d;
|
||||
|
||||
logic [TRANS_ID_BITS-1:0] id_q, id_d;
|
||||
logic [TRANS_ID_BITS-1:0] id_q, id_d;
|
||||
|
||||
logic rem_sel_d, rem_sel_q;
|
||||
logic comp_inv_d, comp_inv_q;
|
||||
logic res_inv_d, res_inv_q;
|
||||
logic rem_sel_d, rem_sel_q;
|
||||
logic comp_inv_d, comp_inv_q;
|
||||
logic res_inv_d, res_inv_q;
|
||||
|
||||
logic [WIDTH-1:0] add_mux;
|
||||
logic [WIDTH-1:0] add_out;
|
||||
logic [WIDTH-1:0] add_tmp;
|
||||
logic [WIDTH-1:0] b_mux;
|
||||
logic [WIDTH-1:0] out_mux;
|
||||
logic [WIDTH-1:0] add_mux;
|
||||
logic [WIDTH-1:0] add_out;
|
||||
logic [WIDTH-1:0] add_tmp;
|
||||
logic [WIDTH-1:0] b_mux;
|
||||
logic [WIDTH-1:0] out_mux;
|
||||
|
||||
logic [$clog2(WIDTH+1)-1:0] cnt_q, cnt_d;
|
||||
logic cnt_zero;
|
||||
logic [$clog2(WIDTH+1)-1:0] cnt_q, cnt_d;
|
||||
logic cnt_zero;
|
||||
|
||||
logic [WIDTH-1:0] lzc_a_input, lzc_b_input, op_b;
|
||||
logic [$clog2(WIDTH)-1:0] lzc_a_result, lzc_b_result;
|
||||
logic [$clog2(WIDTH+1)-1:0] shift_a;
|
||||
logic [$clog2(WIDTH+1):0] div_shift;
|
||||
logic [WIDTH-1:0] lzc_a_input, lzc_b_input, op_b;
|
||||
logic [$clog2(WIDTH)-1:0] lzc_a_result, lzc_b_result;
|
||||
logic [$clog2(WIDTH+1)-1:0] shift_a;
|
||||
logic [$clog2(WIDTH+1):0] div_shift;
|
||||
|
||||
logic a_reg_en, b_reg_en, res_reg_en, ab_comp, pm_sel, load_en;
|
||||
logic lzc_a_no_one, lzc_b_no_one;
|
||||
logic div_res_zero_d, div_res_zero_q;
|
||||
logic a_reg_en, b_reg_en, res_reg_en, ab_comp, pm_sel, load_en;
|
||||
logic lzc_a_no_one, lzc_b_no_one;
|
||||
logic div_res_zero_d, div_res_zero_q;
|
||||
|
||||
|
||||
/////////////////////////////////////
|
||||
|
@ -79,180 +79,179 @@ module serdiv #(
|
|||
// for faster division
|
||||
/////////////////////////////////////
|
||||
|
||||
assign op_b_zero = (op_b_i == 0);
|
||||
assign op_a_sign = op_a_i[$high(op_a_i)];
|
||||
assign op_b_sign = op_b_i[$high(op_b_i)];
|
||||
assign op_b_zero = (op_b_i == 0);
|
||||
assign op_a_sign = op_a_i[$high(op_a_i)];
|
||||
assign op_b_sign = op_b_i[$high(op_b_i)];
|
||||
|
||||
assign lzc_a_input = (opcode_i[0] & op_a_sign) ? {~op_a_i, 1'b0} : op_a_i;
|
||||
assign lzc_b_input = (opcode_i[0] & op_b_sign) ? ~op_b_i : op_b_i;
|
||||
assign lzc_a_input = (opcode_i[0] & op_a_sign) ? {~op_a_i, 1'b0} : op_a_i;
|
||||
assign lzc_b_input = (opcode_i[0] & op_b_sign) ? ~op_b_i : op_b_i;
|
||||
|
||||
lzc #(
|
||||
.MODE ( 1 ), // count leading zeros
|
||||
.WIDTH ( WIDTH )
|
||||
) i_lzc_a (
|
||||
.in_i ( lzc_a_input ),
|
||||
.cnt_o ( lzc_a_result ),
|
||||
.empty_o ( lzc_a_no_one )
|
||||
);
|
||||
lzc #(
|
||||
.MODE ( 1 ), // count leading zeros
|
||||
.WIDTH ( WIDTH )
|
||||
) i_lzc_a (
|
||||
.in_i ( lzc_a_input ),
|
||||
.cnt_o ( lzc_a_result ),
|
||||
.empty_o ( lzc_a_no_one )
|
||||
);
|
||||
|
||||
lzc #(
|
||||
.MODE ( 1 ), // count leading zeros
|
||||
.WIDTH ( WIDTH )
|
||||
) i_lzc_b (
|
||||
.in_i ( lzc_b_input ),
|
||||
.cnt_o ( lzc_b_result ),
|
||||
.empty_o ( lzc_b_no_one )
|
||||
);
|
||||
lzc #(
|
||||
.MODE ( 1 ), // count leading zeros
|
||||
.WIDTH ( WIDTH )
|
||||
) i_lzc_b (
|
||||
.in_i ( lzc_b_input ),
|
||||
.cnt_o ( lzc_b_result ),
|
||||
.empty_o ( lzc_b_no_one )
|
||||
);
|
||||
|
||||
assign shift_a = (lzc_a_no_one) ? WIDTH : lzc_a_result;
|
||||
assign div_shift = (lzc_b_no_one) ? WIDTH : lzc_b_result-shift_a;
|
||||
assign shift_a = (lzc_a_no_one) ? WIDTH : lzc_a_result;
|
||||
assign div_shift = (lzc_b_no_one) ? WIDTH : lzc_b_result-shift_a;
|
||||
|
||||
assign op_b = op_b_i <<< $unsigned(div_shift);
|
||||
assign op_b = op_b_i <<< $unsigned(div_shift);
|
||||
|
||||
// the division is zero if |opB| > |opA| and can be terminated
|
||||
assign div_res_zero_d = (load_en) ? ($signed(div_shift) < 0) : div_res_zero_q;
|
||||
// the division is zero if |opB| > |opA| and can be terminated
|
||||
assign div_res_zero_d = (load_en) ? ($signed(div_shift) < 0) : div_res_zero_q;
|
||||
|
||||
/////////////////////////////////////
|
||||
// Datapath
|
||||
/////////////////////////////////////
|
||||
|
||||
assign pm_sel = load_en & ~(opcode_i[0] & (op_a_sign ^ op_b_sign));
|
||||
assign pm_sel = load_en & ~(opcode_i[0] & (op_a_sign ^ op_b_sign));
|
||||
|
||||
// muxes
|
||||
assign add_mux = (load_en) ? op_a_i : op_b_q;
|
||||
// muxes
|
||||
assign add_mux = (load_en) ? op_a_i : op_b_q;
|
||||
|
||||
// attention: logical shift by one in case of negative operand B!
|
||||
assign b_mux = (load_en) ? op_b : {comp_inv_q, (op_b_q[$high(op_b_q):1])};
|
||||
// attention: logical shift by one in case of negative operand B!
|
||||
assign b_mux = (load_en) ? op_b : {comp_inv_q, (op_b_q[$high(op_b_q):1])};
|
||||
|
||||
// in case of bad timing, we could output from regs -> needs a cycle more in the FSM
|
||||
assign out_mux = (rem_sel_q) ? op_a_q : res_q;
|
||||
// assign out_mux = (rem_sel_q) ? op_a_d : res_d;
|
||||
// in case of bad timing, we could output from regs -> needs a cycle more in the FSM
|
||||
assign out_mux = (rem_sel_q) ? op_a_q : res_q;
|
||||
// assign out_mux = (rem_sel_q) ? op_a_d : res_d;
|
||||
|
||||
// invert if necessary
|
||||
assign res_o = (res_inv_q) ? -$signed(out_mux) : out_mux;
|
||||
// invert if necessary
|
||||
assign res_o = (res_inv_q) ? -$signed(out_mux) : out_mux;
|
||||
|
||||
// main comparator
|
||||
assign ab_comp = ((op_a_q == op_b_q) | ((op_a_q > op_b_q) ^ comp_inv_q)) & ((|op_a_q) | op_b_zero_q);
|
||||
// main comparator
|
||||
assign ab_comp = ((op_a_q == op_b_q) | ((op_a_q > op_b_q) ^ comp_inv_q)) & ((|op_a_q) | op_b_zero_q);
|
||||
|
||||
// main adder
|
||||
assign add_tmp = (load_en) ? 0 : op_a_q;
|
||||
assign add_out = (pm_sel) ? add_tmp + add_mux : add_tmp - $signed(add_mux);
|
||||
// main adder
|
||||
assign add_tmp = (load_en) ? 0 : op_a_q;
|
||||
assign add_out = (pm_sel) ? add_tmp + add_mux : add_tmp - $signed(add_mux);
|
||||
|
||||
/////////////////////////////////////
|
||||
// FSM, counter
|
||||
/////////////////////////////////////
|
||||
|
||||
assign cnt_zero = (cnt_q == 0);
|
||||
assign cnt_d = (load_en) ? div_shift :
|
||||
(~cnt_zero) ? cnt_q - 1 : cnt_q;
|
||||
assign cnt_zero = (cnt_q == 0);
|
||||
assign cnt_d = (load_en) ? div_shift :
|
||||
(~cnt_zero) ? cnt_q - 1 : cnt_q;
|
||||
|
||||
always_comb begin : p_fsm
|
||||
// default
|
||||
state_d = state_q;
|
||||
in_rdy_o = 1'b0;
|
||||
out_vld_o = 1'b0;
|
||||
load_en = 1'b0;
|
||||
a_reg_en = 1'b0;
|
||||
b_reg_en = 1'b0;
|
||||
res_reg_en = 1'b0;
|
||||
always_comb begin : p_fsm
|
||||
// default
|
||||
state_d = state_q;
|
||||
in_rdy_o = 1'b0;
|
||||
out_vld_o = 1'b0;
|
||||
load_en = 1'b0;
|
||||
a_reg_en = 1'b0;
|
||||
b_reg_en = 1'b0;
|
||||
res_reg_en = 1'b0;
|
||||
|
||||
unique case (state_q)
|
||||
IDLE: begin
|
||||
in_rdy_o = 1'b1;
|
||||
unique case (state_q)
|
||||
IDLE: begin
|
||||
in_rdy_o = 1'b1;
|
||||
|
||||
if (in_vld_i) begin
|
||||
in_rdy_o = 1'b0;// there is a cycle delay until the valid signal is asserted by the id stage
|
||||
a_reg_en = 1'b1;
|
||||
b_reg_en = 1'b1;
|
||||
load_en = 1'b1;
|
||||
state_d = DIVIDE;
|
||||
end
|
||||
end
|
||||
DIVIDE: begin
|
||||
if(~div_res_zero_q) begin
|
||||
a_reg_en = ab_comp;
|
||||
b_reg_en = 1'b1;
|
||||
res_reg_en = 1'b1;
|
||||
end
|
||||
// can end the division now if the result is clearly 0
|
||||
if(div_res_zero_q) begin
|
||||
out_vld_o = 1'b1;
|
||||
state_d = FINISH;
|
||||
if(out_rdy_i) begin
|
||||
// in_rdy_o = 1'b1;// there is a cycle delay until the valid signal is asserted by the id stage
|
||||
state_d = IDLE;
|
||||
end
|
||||
end else if (cnt_zero) begin
|
||||
state_d = FINISH;
|
||||
end
|
||||
end
|
||||
FINISH: begin
|
||||
out_vld_o = 1'b1;
|
||||
|
||||
if (out_rdy_i) begin
|
||||
// in_rdy_o = 1'b1;// there is a cycle delay until the valid signal is asserted by the id stage
|
||||
state_d = IDLE;
|
||||
end
|
||||
end
|
||||
default : state_d = IDLE;
|
||||
endcase
|
||||
|
||||
if (flush_i) begin
|
||||
in_rdy_o = 1'b0;
|
||||
out_vld_o = 1'b0;
|
||||
a_reg_en = 1'b0;
|
||||
b_reg_en = 1'b0;
|
||||
load_en = 1'b0;
|
||||
state_d = IDLE;
|
||||
end
|
||||
if (in_vld_i) begin
|
||||
in_rdy_o = 1'b0;// there is a cycle delay until the valid signal is asserted by the id stage
|
||||
a_reg_en = 1'b1;
|
||||
b_reg_en = 1'b1;
|
||||
load_en = 1'b1;
|
||||
state_d = DIVIDE;
|
||||
end
|
||||
end
|
||||
DIVIDE: begin
|
||||
if(~div_res_zero_q) begin
|
||||
a_reg_en = ab_comp;
|
||||
b_reg_en = 1'b1;
|
||||
res_reg_en = 1'b1;
|
||||
end
|
||||
// can end the division now if the result is clearly 0
|
||||
if(div_res_zero_q) begin
|
||||
out_vld_o = 1'b1;
|
||||
state_d = FINISH;
|
||||
if(out_rdy_i) begin
|
||||
// in_rdy_o = 1'b1;// there is a cycle delay until the valid signal is asserted by the id stage
|
||||
state_d = IDLE;
|
||||
end
|
||||
end else if (cnt_zero) begin
|
||||
state_d = FINISH;
|
||||
end
|
||||
end
|
||||
FINISH: begin
|
||||
out_vld_o = 1'b1;
|
||||
|
||||
if (out_rdy_i) begin
|
||||
// in_rdy_o = 1'b1;// there is a cycle delay until the valid signal is asserted by the id stage
|
||||
state_d = IDLE;
|
||||
end
|
||||
end
|
||||
default : state_d = IDLE;
|
||||
endcase
|
||||
|
||||
if (flush_i) begin
|
||||
in_rdy_o = 1'b0;
|
||||
out_vld_o = 1'b0;
|
||||
a_reg_en = 1'b0;
|
||||
b_reg_en = 1'b0;
|
||||
load_en = 1'b0;
|
||||
state_d = IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
/////////////////////////////////////
|
||||
// regs, flags
|
||||
/////////////////////////////////////
|
||||
|
||||
// get flags
|
||||
assign rem_sel_d = (load_en) ? opcode_i[1] : rem_sel_q;
|
||||
assign comp_inv_d = (load_en) ? opcode_i[0] & op_b_sign : comp_inv_q;
|
||||
assign op_b_zero_d = (load_en) ? op_b_zero : op_b_zero_q;
|
||||
assign res_inv_d = (load_en) ? (~op_b_zero | opcode_i[1]) & opcode_i[0] & (op_a_sign ^ op_b_sign) : res_inv_q;
|
||||
// get flags
|
||||
assign rem_sel_d = (load_en) ? opcode_i[1] : rem_sel_q;
|
||||
assign comp_inv_d = (load_en) ? opcode_i[0] & op_b_sign : comp_inv_q;
|
||||
assign op_b_zero_d = (load_en) ? op_b_zero : op_b_zero_q;
|
||||
assign res_inv_d = (load_en) ? (~op_b_zero | opcode_i[1]) & opcode_i[0] & (op_a_sign ^ op_b_sign) : res_inv_q;
|
||||
|
||||
// transaction id
|
||||
assign id_d = (load_en) ? id_i : id_q;
|
||||
assign id_o = id_q;
|
||||
// transaction id
|
||||
assign id_d = (load_en) ? id_i : id_q;
|
||||
assign id_o = id_q;
|
||||
|
||||
assign op_a_d = (a_reg_en) ? add_out : op_a_q;
|
||||
assign op_b_d = (b_reg_en) ? b_mux : op_b_q;
|
||||
assign res_d = (load_en) ? '0 :
|
||||
(res_reg_en) ? {res_q[$high(res_q)-1:0], ab_comp} : res_q;
|
||||
assign op_a_d = (a_reg_en) ? add_out : op_a_q;
|
||||
assign op_b_d = (b_reg_en) ? b_mux : op_b_q;
|
||||
assign res_d = (load_en) ? '0 :
|
||||
(res_reg_en) ? {res_q[$high(res_q)-1:0], ab_comp} : res_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
|
||||
if (~rst_ni) begin
|
||||
state_q <= IDLE;
|
||||
op_a_q <= '0;
|
||||
op_b_q <= '0;
|
||||
res_q <= '0;
|
||||
cnt_q <= '0;
|
||||
id_q <= '0;
|
||||
rem_sel_q <= 1'b0;
|
||||
comp_inv_q <= 1'b0;
|
||||
res_inv_q <= 1'b0;
|
||||
op_b_zero_q <= 1'b0;
|
||||
div_res_zero_q <= 1'b0;
|
||||
end else begin
|
||||
state_q <= state_d;
|
||||
op_a_q <= op_a_d;
|
||||
op_b_q <= op_b_d;
|
||||
res_q <= res_d;
|
||||
cnt_q <= cnt_d;
|
||||
id_q <= id_d;
|
||||
rem_sel_q <= rem_sel_d;
|
||||
comp_inv_q <= comp_inv_d;
|
||||
res_inv_q <= res_inv_d;
|
||||
op_b_zero_q <= op_b_zero_d;
|
||||
div_res_zero_q <= div_res_zero_d;
|
||||
end
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
|
||||
if (~rst_ni) begin
|
||||
state_q <= IDLE;
|
||||
op_a_q <= '0;
|
||||
op_b_q <= '0;
|
||||
res_q <= '0;
|
||||
cnt_q <= '0;
|
||||
id_q <= '0;
|
||||
rem_sel_q <= 1'b0;
|
||||
comp_inv_q <= 1'b0;
|
||||
res_inv_q <= 1'b0;
|
||||
op_b_zero_q <= 1'b0;
|
||||
div_res_zero_q <= 1'b0;
|
||||
end else begin
|
||||
state_q <= state_d;
|
||||
op_a_q <= op_a_d;
|
||||
op_b_q <= op_b_d;
|
||||
res_q <= res_d;
|
||||
cnt_q <= cnt_d;
|
||||
id_q <= id_d;
|
||||
rem_sel_q <= rem_sel_d;
|
||||
comp_inv_q <= comp_inv_d;
|
||||
res_inv_q <= res_inv_d;
|
||||
op_b_zero_q <= op_b_zero_d;
|
||||
div_res_zero_q <= div_res_zero_d;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
7
tb/tb_serdiv/.gitignore
vendored
Normal file
7
tb/tb_serdiv/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
work
|
||||
*.rep
|
||||
transcript
|
||||
*.ini
|
||||
*.wlf
|
||||
*.log
|
||||
|
|
@ -1,16 +1,30 @@
|
|||
# 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
# Date: 15.08.2018
|
||||
# Description: Makefile for the serial divider testbench.
|
||||
|
||||
library ?= work
|
||||
toplevel ?= tb
|
||||
src-list := tb.list
|
||||
inc-path := $(shell pwd)/hdl/
|
||||
src := $(shell xargs printf '\n%s' < $(src-list) | cut -b 1-)
|
||||
compile_flag += +cover+i_dut -incr -64 -nologo
|
||||
compile_flag += +cover+i_dut -incr -64 -nologo
|
||||
sim_opts += -64 -coverage -classdebug -voptargs="+acc"
|
||||
questa_version ?= ${QUESTASIM_VERSION}
|
||||
|
||||
incdir += ../common/
|
||||
|
||||
build: clean
|
||||
vlib${questa_version} $(library)
|
||||
vlog${questa_version} -work $(library) -pedanticerrors $(src) $(compile_flag) +incdir+$(inc-path)
|
||||
vlog${questa_version} -work $(library) -pedanticerrors $(src) $(compile_flag) +incdir+$(incdir)
|
||||
touch $(library)/.build
|
||||
|
||||
sim: build
|
||||
|
@ -19,7 +33,7 @@ sim: build
|
|||
simc: build
|
||||
vsim${questa_version} -lib $(library) $(toplevel) -c -do "run -all; exit" $(sim_opts)
|
||||
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf $(library)
|
||||
|
||||
|
|
6
tb/tb_serpent_dcache/.gitignore
vendored
6
tb/tb_serpent_dcache/.gitignore
vendored
|
@ -1,3 +1,7 @@
|
|||
work
|
||||
modelsim.ini
|
||||
*.rep
|
||||
transcript
|
||||
*.ini
|
||||
*.wlf
|
||||
*.log
|
||||
|
||||
|
|
|
@ -1,25 +1,40 @@
|
|||
# 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
# Date: 15.08.2018
|
||||
# Description: Makefile for the dcache testbench.
|
||||
|
||||
library ?= work
|
||||
toplevel ?= tb
|
||||
src-list := tb.list
|
||||
inc-path := $(shell pwd)/hdl/
|
||||
src := $(shell xargs printf '\n%s' < $(src-list) | cut -b 1-)
|
||||
compile_flag += +cover+i_dut -incr -64 -nologo
|
||||
compile_flag += +cover+i_dut -incr -64 -nologo
|
||||
sim_opts += -64 -coverage -classdebug -voptargs="+acc"
|
||||
questa_version ?= ${QUESTASIM_VERSION}
|
||||
|
||||
incdir += ../common/
|
||||
|
||||
build: clean
|
||||
vlib${questa_version} $(library)
|
||||
vlog${questa_version} -work $(library) -pedanticerrors $(src) $(compile_flag) +incdir+$(inc-path)
|
||||
vlog${questa_version} -work $(library) -pedanticerrors $(src) $(compile_flag) +incdir+$(incdir)
|
||||
touch $(library)/.build
|
||||
|
||||
# this starts modelsim with gui
|
||||
sim: build
|
||||
vsim${questa_version} -lib $(library) $(toplevel) -do "do wave.do" $(sim_opts)
|
||||
|
||||
# batch mode without gui
|
||||
simc: build
|
||||
vsim${questa_version} -lib $(library) $(toplevel) -c -do "run -all; exit" $(sim_opts)
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf $(library)
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
//
|
||||
// Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
// Description:
|
||||
//
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// use to ensure proper ATI timing
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
`define APPL_ACQ_WAIT #(ACQ_DEL-APPL_DEL);
|
||||
|
||||
`define WAIT_CYC(CLK, N) \
|
||||
repeat(N) @(posedge(CLK));
|
||||
|
||||
`define WAIT(CLK, SIG) \
|
||||
do begin \
|
||||
@(posedge(CLK)); \
|
||||
end while(SIG == 1'b0);
|
||||
|
||||
`define WAIT_SIG(CLK,SIG) \
|
||||
do begin \
|
||||
@(posedge(CLK)); \
|
||||
end while(SIG == 1'b0);
|
||||
|
||||
`define APPL_WAIT_COMB_SIG(CLK,SIG) \
|
||||
`APPL_ACQ_WAIT \
|
||||
while(SIG == 1'b0) begin \
|
||||
@(posedge(CLK)); \
|
||||
#(ACQ_DEL); \
|
||||
end
|
||||
|
||||
`define APPL_WAIT_SIG(CLK,SIG) \
|
||||
do begin \
|
||||
@(posedge(CLK)); \
|
||||
#(APPL_DEL); \
|
||||
end while(SIG == 1'b0);
|
||||
|
||||
`define ACQ_WAIT_SIG(CLK,SIG) \
|
||||
do begin \
|
||||
@(posedge(CLK)); \
|
||||
#(ACQ_DEL); \
|
||||
end while(SIG == 1'b0);
|
||||
|
||||
|
||||
`define APPL_WAIT_CYC(CLK, N) \
|
||||
repeat(N) @(posedge(CLK)); \
|
||||
#(tb_pkg::APPL_DEL);
|
||||
|
||||
`define ACQ_WAIT_CYC(CLK, N) \
|
||||
repeat(N) @(posedge(CLK)); \
|
||||
#(tb_pkg::ACQ_DEL);
|
||||
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
@ -25,7 +21,7 @@ import serpent_cache_pkg::*;
|
|||
import tb_pkg::*;
|
||||
|
||||
module tb_mem #(
|
||||
parameter string MemName = "TB_MEM",
|
||||
parameter string MemName = "TB_MEM",
|
||||
parameter MemRandHitRate = 10, //in percent
|
||||
parameter MemRandInvRate = 5, //in percent
|
||||
parameter MemWords = 1024*1024,// in 64bit words
|
||||
|
@ -75,7 +71,7 @@ module tb_mem #(
|
|||
logic [63:0] mem_array_q[MemWords-1:0];
|
||||
|
||||
// this shadow memory provides a view that is consistent with the one from the core
|
||||
// i.e., pending writes are present in this view, and invalidations will not overwrite
|
||||
// i.e., pending writes are present in this view, and invalidations will not overwrite
|
||||
// the corresponding bytes until they have been commited to the normal memory.
|
||||
logic [63:0] mem_array_shadow_q[MemWords-1:0];
|
||||
logic [7:0] mem_array_dirty_q[MemWords-1:0];
|
||||
|
@ -85,8 +81,8 @@ module tb_mem #(
|
|||
// sequential process holding the state of the memory readout process
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : p_tlb_rand
|
||||
automatic int rnd = 0;
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [63:0] lval;
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [63:0] lval;
|
||||
|
||||
if(~rst_ni) begin
|
||||
mem_ready_q <= '0;
|
||||
|
@ -94,26 +90,26 @@ module tb_mem #(
|
|||
rand_addr_q <= '0;
|
||||
initialized_q <= '0;
|
||||
end else begin
|
||||
|
||||
|
||||
// fill the memory once with random data
|
||||
if (initialized_q) begin
|
||||
// commit "virtual" writes (i.e., clear the dirty flags)
|
||||
if(commit_en_i) begin
|
||||
for(int k=0; k<8; k++) begin
|
||||
if(commit_be_i[k]) begin
|
||||
mem_array_dirty_q[commit_paddr_i>>3][k] <= 1'b0;
|
||||
mem_array_dirty_q[commit_paddr_i>>3][k] <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// "virtual" writes coming from TB agent, used to generate expected responses
|
||||
if(write_en_i) begin
|
||||
for(int k=0; k<8; k++) begin
|
||||
if(write_be_i[k]) begin
|
||||
mem_array_shadow_q[write_paddr_i>>3][k*8 +: 8] <= write_data_i[k*8 +: 8];
|
||||
mem_array_dirty_q[write_paddr_i>>3][k] <= 1'b1;
|
||||
mem_array_dirty_q[write_paddr_i>>3][k] <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// "real" writes coming via the miss controller
|
||||
|
@ -125,22 +121,22 @@ module tb_mem #(
|
|||
3'b011: mem_array_q[outfifo_data.paddr>>3] = outfifo_data.data[0 +: 64];
|
||||
default: begin
|
||||
$fatal(1,"unsupported transfer size for write");
|
||||
end
|
||||
endcase // infifo_data.size
|
||||
end
|
||||
// initialization with random data
|
||||
end
|
||||
endcase // infifo_data.size
|
||||
end
|
||||
// initialization with random data
|
||||
end else begin
|
||||
mem_array_dirty_q <= '{default:'0};
|
||||
|
||||
|
||||
for (int k=0; k<MemWords; k++) begin
|
||||
void'(randomize(val));
|
||||
mem_array_q[k] <= val;
|
||||
mem_array_shadow_q[k] <= val;
|
||||
end
|
||||
initialized_q <= 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
// generate random contentions
|
||||
if (mem_rand_en_i) begin
|
||||
void'(randomize(rnd) with {rnd > 0; rnd <= 100;});
|
||||
|
@ -150,7 +146,7 @@ module tb_mem #(
|
|||
mem_ready_q <= '0;
|
||||
end
|
||||
end else begin
|
||||
mem_ready_q <= '1;
|
||||
mem_ready_q <= '1;
|
||||
end
|
||||
|
||||
// generate random invalidations
|
||||
|
@ -161,20 +157,20 @@ module tb_mem #(
|
|||
void'(randomize(lval) with {lval>=0; lval<(MemWords>>3);});
|
||||
void'(randomize(val));
|
||||
rand_addr_q <= lval<<3;
|
||||
|
||||
// with the current TB setup, we cannot invalidate a memory location if a write response to the same address is
|
||||
|
||||
// with the current TB setup, we cannot invalidate a memory location if a write response to the same address is
|
||||
// in flight, since this could lead to an incosistent state between the real memory and the shadow memory view.
|
||||
// the workaround is not to overwrite shadow memory regions that are still pending in the write buffer
|
||||
// the workaround is not to overwrite shadow memory regions that are still pending in the write buffer
|
||||
// this can be improved.
|
||||
for(int k=0; k<8; k++) begin
|
||||
if(~mem_array_dirty_q[lval][k]) begin
|
||||
mem_array_q [lval][k*8 +: 8] <= val[k*8 +: 8];
|
||||
mem_array_shadow_q[lval][k*8 +: 8] <= val[k*8 +: 8];
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
mem_inv_q <= '0;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
mem_inv_q <= '0;
|
||||
end
|
||||
|
@ -217,7 +213,7 @@ module tb_mem #(
|
|||
infifo_push = 1'b1;
|
||||
|
||||
end else if ((~outfifo_empty) && (~infifo_full) && mem_ready_q) begin
|
||||
|
||||
|
||||
outfifo_pop = 1'b1;
|
||||
infifo_push = 1'b1;
|
||||
|
||||
|
@ -233,20 +229,20 @@ module tb_mem #(
|
|||
3'b011: infifo_data.data[0+:64] = mem_array_q[outfifo_data.paddr>>3];
|
||||
3'b111: for(int k=0; k<DCACHE_LINE_WIDTH/64; k++) infifo_data.data[k*64 +:64] = mem_array_q[(outfifo_data.paddr>>3) + k];
|
||||
default: $fatal(1,"unsupported transfer size for read");
|
||||
endcase // infifo_data.size
|
||||
end
|
||||
endcase // infifo_data.size
|
||||
end
|
||||
DCACHE_STORE_REQ: begin
|
||||
infifo_data.tid = outfifo_data.tid;
|
||||
infifo_data.rtype = DCACHE_STORE_ACK;
|
||||
infifo_data.nc = outfifo_data.nc;
|
||||
write_en = 1'b1;
|
||||
end
|
||||
// DCACHE_ATOMIC_REQ: $fatal(1, "DCACHE_ATOMIC_REQ not implemented yet");
|
||||
// DCACHE_INT_REQ: $fatal(1, "DCACHE_INT_REQ not implemented yet");
|
||||
// DCACHE_ATOMIC_REQ: $fatal(1, "DCACHE_ATOMIC_REQ not implemented yet");
|
||||
// DCACHE_INT_REQ: $fatal(1, "DCACHE_INT_REQ not implemented yet");
|
||||
default: begin
|
||||
// $fatal(1, "unsupported request type");
|
||||
end
|
||||
endcase // outfifo_data.rtype
|
||||
// $fatal(1, "unsupported request type");
|
||||
end
|
||||
endcase // outfifo_data.rtype
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -301,10 +297,10 @@ module tb_mem #(
|
|||
bit ok;
|
||||
progress status;
|
||||
status = new(MemName);
|
||||
|
||||
|
||||
`ACQ_WAIT_CYC(clk_i,10)
|
||||
`ACQ_WAIT_SIG(clk_i,~rst_ni)
|
||||
|
||||
|
||||
while(~seq_last_i) begin
|
||||
`ACQ_WAIT_SIG(clk_i,check_en_i)
|
||||
status.reset(MemWords);
|
||||
|
@ -312,15 +308,15 @@ module tb_mem #(
|
|||
for(int k=0; k<MemWords; k++) begin
|
||||
ok = (mem_array_q[k] == mem_array_shadow_q[k]) && !(|mem_array_dirty_q[k]);
|
||||
if(!ok) begin
|
||||
$display("%s> dirty bytes at k=%016X: real[k>>3]=%016X, shadow[k>>3]=%016X, dirty[k>>3]=%02X",
|
||||
$display("%s> dirty bytes at k=%016X: real[k>>3]=%016X, shadow[k>>3]=%016X, dirty[k>>3]=%02X",
|
||||
MemName, k<<3, mem_array_q[k], mem_array_shadow_q[k], mem_array_dirty_q[k]);
|
||||
end
|
||||
status.addRes(!ok);
|
||||
status.print();
|
||||
end
|
||||
end
|
||||
status.printToFile({MemName, "_summary.rep"}, 1);
|
||||
|
||||
end
|
||||
end
|
||||
status.printToFile({MemName, "_summary.rep"}, 1);
|
||||
|
||||
if(status.totErrCnt == 0) begin
|
||||
$display("%s> ----------------------------------------------------------------------", MemName);
|
||||
$display("%s> PASSED %0d VECTORS", MemName, status.totAcqCnt);
|
||||
|
@ -330,10 +326,10 @@ module tb_mem #(
|
|||
$display("%s> FAILED %0d OF %0d VECTORS\n", MemName , status.totErrCnt, status.totAcqCnt);
|
||||
$display("%s> ----------------------------------------------------------------------\n", MemName);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// assertions
|
||||
|
@ -343,23 +339,23 @@ module tb_mem #(
|
|||
`ifndef verilator
|
||||
|
||||
nc_region: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.paddr >= CachedAddrEnd || mem_data_i.paddr < CachedAddrBeg |-> mem_data_i.nc)
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.paddr >= CachedAddrEnd || mem_data_i.paddr < CachedAddrBeg |-> mem_data_i.nc)
|
||||
else $fatal(1, "cached access into noncached region");
|
||||
|
||||
cached_reads: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype==DCACHE_LOAD_REQ |-> ~mem_data_i.nc |-> mem_data_i.size == 3'b111)
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype==DCACHE_LOAD_REQ |-> ~mem_data_i.nc |-> mem_data_i.size == 3'b111)
|
||||
else $fatal(1, "cached read accesses always have to be one CL wide");
|
||||
|
||||
nc_reads: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype==DCACHE_LOAD_REQ |-> mem_data_i.nc |-> mem_data_i.size inside {3'b000, 3'b001, 3'b010, 3'b011})
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype==DCACHE_LOAD_REQ |-> mem_data_i.nc |-> mem_data_i.size inside {3'b000, 3'b001, 3'b010, 3'b011})
|
||||
else $fatal(1, "nc read size can only be one of the following: byte, halfword, word, dword");
|
||||
|
||||
write_size: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype==DCACHE_STORE_REQ |-> mem_data_i.size inside {3'b000, 3'b001, 3'b010, 3'b011})
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype==DCACHE_STORE_REQ |-> mem_data_i.size inside {3'b000, 3'b001, 3'b010, 3'b011})
|
||||
else $fatal(1, "write size can only be one of the following: byte, halfword, word, dword");
|
||||
|
||||
addr_range: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype inside {DCACHE_STORE_REQ, DCACHE_STORE_REQ} |-> mem_data_i.paddr < (MemWords<<3))
|
||||
@(posedge clk_i) disable iff (~rst_ni) mem_data_req_i |-> mem_data_i.rtype inside {DCACHE_STORE_REQ, DCACHE_STORE_REQ} |-> mem_data_i.paddr < (MemWords<<3))
|
||||
else $fatal(1, "address is out of bounds");
|
||||
|
||||
`endif
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
@ -20,7 +16,7 @@
|
|||
package tb_pkg;
|
||||
|
||||
// // for abs(double) function
|
||||
// import mti_cstdlib::*;
|
||||
// import mti_cstdlib::*;
|
||||
|
||||
// for timestamps
|
||||
import "DPI-C" \time = function int _time (inout int tloc[4]);
|
||||
|
@ -29,13 +25,13 @@ package tb_pkg;
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// parameters
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// creates a 10ns ATI timing cycle
|
||||
time CLK_HI = 5ns; // set clock high time
|
||||
time CLK_LO = 5ns; // set clock low time
|
||||
|
||||
// creates a 10ns ATI timing cycle
|
||||
time CLK_HI = 5ns; // set clock high time
|
||||
time CLK_LO = 5ns; // set clock low time
|
||||
time CLK_PERIOD = CLK_HI+CLK_LO;
|
||||
time APPL_DEL = 2ns; // set stimuli application delay
|
||||
time ACQ_DEL = 8ns; // set response aquisition delay
|
||||
time APPL_DEL = 2ns; // set stimuli application delay
|
||||
time ACQ_DEL = 8ns; // set response aquisition delay
|
||||
|
||||
parameter ERROR_CNT_STOP_LEVEL = 1; // use 1 for debugging. 0 runs the complete simulation...
|
||||
|
||||
|
@ -43,9 +39,9 @@ package tb_pkg;
|
|||
typedef enum logic [2:0] { RANDOM_SEQ, LINEAR_SEQ, BURST_SEQ, IDLE_SEQ, WRAP_SEQ } seq_t;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// progress
|
||||
// progress
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class progress;
|
||||
real newState, oldState;
|
||||
longint numResp, acqCnt, errCnt, totAcqCnt, totErrCnt;
|
||||
|
@ -62,20 +58,20 @@ package tb_pkg;
|
|||
this.totAcqCnt = 0;
|
||||
this.totErrCnt = 0;
|
||||
|
||||
end
|
||||
end
|
||||
endfunction : new
|
||||
|
||||
|
||||
function void reset(longint numResp_);
|
||||
begin
|
||||
this.acqCnt = 0;
|
||||
this.errCnt = 0;
|
||||
this.newState = 0.0;
|
||||
this.oldState = 0.0;
|
||||
this.numResp = numResp_;
|
||||
end
|
||||
this.numResp = numResp_;
|
||||
end
|
||||
endfunction : reset
|
||||
|
||||
function void addRes(int isError);
|
||||
function void addRes(int isError);
|
||||
begin
|
||||
this.acqCnt++;
|
||||
this.totAcqCnt++;
|
||||
|
@ -83,16 +79,16 @@ package tb_pkg;
|
|||
this.totErrCnt += isError;
|
||||
|
||||
if(ERROR_CNT_STOP_LEVEL <= this.errCnt && ERROR_CNT_STOP_LEVEL > 0) begin
|
||||
$error("%s> simulation stopped (ERROR_CNT_STOP_LEVEL = %d reached).", this.name, ERROR_CNT_STOP_LEVEL);
|
||||
$error("%s> simulation stopped (ERROR_CNT_STOP_LEVEL = %d reached).", this.name, ERROR_CNT_STOP_LEVEL);
|
||||
$stop();
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
endfunction : addRes
|
||||
|
||||
function void print();
|
||||
function void print();
|
||||
begin
|
||||
this.newState = $itor(this.acqCnt) / $itor(this.numResp);
|
||||
if(this.newState - this.oldState >= 0.01) begin
|
||||
if(this.newState - this.oldState >= 0.01) begin
|
||||
$display("%s> validated %03d%% -- %01d failed (%03.3f%%) ",
|
||||
this.name,
|
||||
$rtoi(this.newState*100.0),
|
||||
|
@ -104,10 +100,10 @@ package tb_pkg;
|
|||
end
|
||||
endfunction : print
|
||||
|
||||
function void printToFile(string file, bit summary = 0);
|
||||
function void printToFile(string file, bit summary = 0);
|
||||
begin
|
||||
int fptr;
|
||||
|
||||
|
||||
// sanitize string
|
||||
for(fptr=0; fptr<$size(file);fptr++) begin
|
||||
if(file[fptr] == " " || file[fptr] == "/" || file[fptr] == "\\") begin
|
||||
|
@ -128,13 +124,13 @@ package tb_pkg;
|
|||
end else begin
|
||||
$fdisplay(fptr, "CI: FAILED");
|
||||
end
|
||||
end else begin
|
||||
end else begin
|
||||
$fdisplay(fptr, "test name: %s", file);
|
||||
$fdisplay(fptr, "this test: %01d of %01d vectors failed (%03.3f%%) ",
|
||||
this.errCnt,
|
||||
this.acqCnt,
|
||||
$itor(this.errCnt) / $itor(this.acqCnt) * 100.0);
|
||||
|
||||
|
||||
$fdisplay(fptr, "total so far: %01d of %01d vectors failed (%03.3f%%) ",
|
||||
this.totErrCnt,
|
||||
this.totAcqCnt,
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
@ -26,379 +22,367 @@ import serpent_cache_pkg::*;
|
|||
import tb_pkg::*;
|
||||
|
||||
program tb_readport #(
|
||||
parameter string PortName = "read port 0",
|
||||
parameter FlushRate = 1,
|
||||
parameter KillRate = 5,
|
||||
parameter TlbHitRate = 95,
|
||||
parameter MemWords = 1024*1024,// in 64bit words
|
||||
parameter logic [63:0] CachedAddrBeg = 0,
|
||||
parameter logic [63:0] CachedAddrEnd = 0,
|
||||
parameter RndSeed = 1110,
|
||||
parameter Verbose = 0
|
||||
parameter string PortName = "read port 0",
|
||||
parameter FlushRate = 1,
|
||||
parameter KillRate = 5,
|
||||
parameter TlbHitRate = 95,
|
||||
parameter MemWords = 1024*1024,// in 64bit words
|
||||
parameter logic [63:0] CachedAddrBeg = 0,
|
||||
parameter logic [63:0] CachedAddrEnd = 0,
|
||||
parameter RndSeed = 1110,
|
||||
parameter Verbose = 0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
||||
// to testbench master
|
||||
ref string test_name_i,
|
||||
input logic [6:0] req_rate_i, //a rate between 0 and 100 percent
|
||||
input seq_t seq_type_i,
|
||||
input logic tlb_rand_en_i,
|
||||
input logic flush_rand_en_i,
|
||||
input logic seq_run_i,
|
||||
input logic [31:0] seq_num_resp_i,
|
||||
input logic seq_last_i,
|
||||
output logic seq_done_o,
|
||||
// to testbench master
|
||||
ref string test_name_i,
|
||||
input logic [6:0] req_rate_i, //a rate between 0 and 100 percent
|
||||
input seq_t seq_type_i,
|
||||
input logic tlb_rand_en_i,
|
||||
input logic flush_rand_en_i,
|
||||
input logic seq_run_i,
|
||||
input logic [31:0] seq_num_resp_i,
|
||||
input logic seq_last_i,
|
||||
output logic seq_done_o,
|
||||
|
||||
// expresp interface
|
||||
output logic [63:0] exp_paddr_o,
|
||||
input logic [1:0] exp_size_i,
|
||||
input logic [63:0] exp_rdata_i,
|
||||
input logic [63:0] exp_paddr_i,
|
||||
input logic [63:0] act_paddr_i,
|
||||
// expresp interface
|
||||
output logic [63:0] exp_paddr_o,
|
||||
input logic [1:0] exp_size_i,
|
||||
input logic [63:0] exp_rdata_i,
|
||||
input logic [63:0] exp_paddr_i,
|
||||
input logic [63:0] act_paddr_i,
|
||||
|
||||
// interface to DUT
|
||||
output logic flush_o,
|
||||
input logic flush_ack_i,
|
||||
output dcache_req_i_t dut_req_port_o,
|
||||
input dcache_req_o_t dut_req_port_i
|
||||
);
|
||||
// interface to DUT
|
||||
output logic flush_o,
|
||||
input logic flush_ack_i,
|
||||
output dcache_req_i_t dut_req_port_o,
|
||||
input dcache_req_o_t dut_req_port_i
|
||||
);
|
||||
|
||||
// leave this
|
||||
timeunit 1ps;
|
||||
timeprecision 1ps;
|
||||
// leave this
|
||||
timeunit 1ps;
|
||||
timeprecision 1ps;
|
||||
|
||||
logic [63:0] paddr;
|
||||
logic seq_end_req, seq_end_ack, prog_end;
|
||||
logic [DCACHE_TAG_WIDTH-1:0] tag_q;
|
||||
logic [DCACHE_TAG_WIDTH-1:0] tag_vld_q;
|
||||
logic [63:0] paddr;
|
||||
logic seq_end_req, seq_end_ack, prog_end;
|
||||
logic [DCACHE_TAG_WIDTH-1:0] tag_q;
|
||||
logic [DCACHE_TAG_WIDTH-1:0] tag_vld_q;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Randomly delay the tag by at least one cycle
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// // TODO: add randomization
|
||||
initial begin : p_tag_delay
|
||||
logic [63:0] tmp_paddr, val;
|
||||
int unsigned cnt;
|
||||
logic tmp_vld;
|
||||
// // TODO: add randomization
|
||||
initial begin : p_tag_delay
|
||||
logic [63:0] tmp_paddr, val;
|
||||
int unsigned cnt;
|
||||
logic tmp_vld;
|
||||
|
||||
tag_q <= '0;
|
||||
tag_vld_q <= 1'b0;
|
||||
tag_q <= '0;
|
||||
tag_vld_q <= 1'b0;
|
||||
|
||||
`APPL_WAIT_CYC(clk_i, 10)
|
||||
`APPL_WAIT_SIG(clk_i,~rst_ni)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
`APPL_WAIT_CYC(clk_i, 10)
|
||||
`APPL_WAIT_SIG(clk_i,~rst_ni)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
|
||||
tmp_vld = 0;
|
||||
cnt = 0;
|
||||
forever begin
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
|
||||
if(cnt==0) begin
|
||||
if(tmp_vld) begin
|
||||
tmp_vld = 0;
|
||||
tag_q <= tmp_paddr[DCACHE_TAG_WIDTH+DCACHE_INDEX_WIDTH-1:DCACHE_INDEX_WIDTH];
|
||||
tag_vld_q <= 1'b1;
|
||||
end else begin
|
||||
tag_vld_q <= 1'b0;
|
||||
end
|
||||
|
||||
`APPL_ACQ_WAIT;
|
||||
if(dut_req_port_o.data_req) begin
|
||||
tmp_paddr = paddr;
|
||||
tmp_vld = 1;
|
||||
|
||||
if(tlb_rand_en_i) begin
|
||||
void'(randomize(val) with {val>0; val<=100;});
|
||||
if(val>=TlbHitRate) begin
|
||||
void'(randomize(cnt) with {cnt>0; cnt<=50;});
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end else begin
|
||||
tag_vld_q <= 1'b0;
|
||||
cnt -= 1;
|
||||
`APPL_ACQ_WAIT;
|
||||
end
|
||||
|
||||
if(dut_req_port_o.kill_req) begin
|
||||
tmp_vld = 0;
|
||||
cnt = 0;
|
||||
end
|
||||
tmp_vld = 0;
|
||||
cnt = 0;
|
||||
forever begin
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
|
||||
if(cnt==0) begin
|
||||
if(tmp_vld) begin
|
||||
tmp_vld = 0;
|
||||
tag_q <= tmp_paddr[DCACHE_TAG_WIDTH+DCACHE_INDEX_WIDTH-1:DCACHE_INDEX_WIDTH];
|
||||
tag_vld_q <= 1'b1;
|
||||
end else begin
|
||||
tag_vld_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
assign dut_req_port_o.address_tag = tag_q;
|
||||
assign dut_req_port_o.tag_valid = tag_vld_q;
|
||||
assign dut_req_port_o.address_index = paddr[DCACHE_INDEX_WIDTH-1:0];
|
||||
assign exp_paddr_o = paddr;
|
||||
`APPL_ACQ_WAIT;
|
||||
if(dut_req_port_o.data_req) begin
|
||||
tmp_paddr = paddr;
|
||||
tmp_vld = 1;
|
||||
|
||||
if(tlb_rand_en_i) begin
|
||||
void'(randomize(val) with {val>0; val<=100;});
|
||||
if(val>=TlbHitRate) begin
|
||||
void'(randomize(cnt) with {cnt>0; cnt<=50;});
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
tag_vld_q <= 1'b0;
|
||||
cnt -= 1;
|
||||
`APPL_ACQ_WAIT;
|
||||
end
|
||||
|
||||
if(dut_req_port_o.kill_req) begin
|
||||
tmp_vld = 0;
|
||||
cnt = 0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign dut_req_port_o.address_tag = tag_q;
|
||||
assign dut_req_port_o.tag_valid = tag_vld_q;
|
||||
assign dut_req_port_o.address_index = paddr[DCACHE_INDEX_WIDTH-1:0];
|
||||
assign exp_paddr_o = paddr;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helper tasks
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
task automatic flushCache();
|
||||
flush_o = 1'b1;
|
||||
`APPL_WAIT_SIG(clk_i, flush_ack_i);
|
||||
flush_o = 0'b0;
|
||||
task automatic flushCache();
|
||||
flush_o = 1'b1;
|
||||
`APPL_WAIT_SIG(clk_i, flush_ack_i);
|
||||
flush_o = 0'b0;
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
endtask : flushCache
|
||||
|
||||
|
||||
task automatic genRandReq();
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [1:0] size;
|
||||
|
||||
void'($urandom(RndSeed));
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
|
||||
while(~seq_end_req) begin
|
||||
// randomize request
|
||||
dut_req_port_o.data_req = '0;
|
||||
// generate random control events
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < KillRate) begin
|
||||
dut_req_port_o.kill_req = 1'b1;
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
endtask : flushCache
|
||||
dut_req_port_o.kill_req = 1'b0;
|
||||
end else begin
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < FlushRate && flush_rand_en_i) begin
|
||||
flushCache();
|
||||
end else begin
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address
|
||||
void'(randomize(val) with {val >= 0; val < (MemWords<<3);});
|
||||
void'(randomize(size));
|
||||
|
||||
|
||||
task automatic genRandReq();
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [1:0] size;
|
||||
|
||||
void'($urandom(RndSeed));
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
|
||||
while(~seq_end_req) begin
|
||||
// randomize request
|
||||
dut_req_port_o.data_req = '0;
|
||||
// generate random control events
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < KillRate) begin
|
||||
dut_req_port_o.kill_req = 1'b1;
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
dut_req_port_o.kill_req = 1'b0;
|
||||
end else begin
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < FlushRate && flush_rand_en_i) begin
|
||||
flushCache();
|
||||
end else begin
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address
|
||||
void'(randomize(val) with {val >= 0; val < (MemWords<<3);});
|
||||
void'(randomize(size));
|
||||
|
||||
dut_req_port_o.data_size = size;
|
||||
paddr = val;
|
||||
|
||||
// align to size
|
||||
unique case(size)
|
||||
2'b01: paddr[0] = 1'b0;
|
||||
2'b10: paddr[1:0] = 2'b00;
|
||||
2'b11: paddr[2:0] = 3'b000;
|
||||
default: ;
|
||||
endcase
|
||||
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
|
||||
endtask : genRandReq
|
||||
|
||||
task automatic genSeqRead();
|
||||
automatic logic [63:0] val;
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
val = '0;
|
||||
while(~seq_end_req) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
dut_req_port_o.data_size = 2'b11;
|
||||
dut_req_port_o.data_size = size;
|
||||
paddr = val;
|
||||
// generate linear read
|
||||
val = (val + 8) % (MemWords<<3);
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
endtask : genSeqRead
|
||||
|
||||
task automatic genWrapSeq();
|
||||
automatic logic [63:0] val;
|
||||
paddr = CachedAddrBeg;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
val = '0;
|
||||
while(~seq_end_req) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
dut_req_port_o.data_size = 2'b11;
|
||||
paddr = val;
|
||||
// generate wrapping read of 1 cachelines
|
||||
paddr = CachedAddrBeg + val;
|
||||
val = (val + 8) % (1*(DCACHE_LINE_WIDTH/64)*8);
|
||||
// align to size
|
||||
unique case(size)
|
||||
2'b01: paddr[0] = 1'b0;
|
||||
2'b10: paddr[1:0] = 2'b00;
|
||||
2'b11: paddr[2:0] = 3'b000;
|
||||
default: ;
|
||||
endcase
|
||||
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
endtask : genWrapSeq
|
||||
end
|
||||
end
|
||||
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
|
||||
endtask : genRandReq
|
||||
|
||||
task automatic genSeqRead();
|
||||
automatic logic [63:0] val;
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
val = '0;
|
||||
while(~seq_end_req) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
dut_req_port_o.data_size = 2'b11;
|
||||
paddr = val;
|
||||
// generate linear read
|
||||
val = (val + 8) % (MemWords<<3);
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
endtask : genSeqRead
|
||||
|
||||
task automatic genWrapSeq();
|
||||
automatic logic [63:0] val;
|
||||
paddr = CachedAddrBeg;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
val = '0;
|
||||
while(~seq_end_req) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
dut_req_port_o.data_size = 2'b11;
|
||||
paddr = val;
|
||||
// generate wrapping read of 1 cachelines
|
||||
paddr = CachedAddrBeg + val;
|
||||
val = (val + 8) % (1*(DCACHE_LINE_WIDTH/64)*8);
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
endtask : genWrapSeq
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Sequence application
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
initial begin : p_stim
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_wdata = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_we = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
seq_end_ack = '0;
|
||||
flush_o = '0;
|
||||
initial begin : p_stim
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_wdata = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_we = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
seq_end_ack = '0;
|
||||
flush_o = '0;
|
||||
|
||||
// print some info
|
||||
$display("%s> current configuration:", PortName);
|
||||
$display("%s> KillRate %d", PortName, KillRate);
|
||||
$display("%s> FlushRate %d", PortName, FlushRate);
|
||||
$display("%s> TlbHitRate %d", PortName, TlbHitRate);
|
||||
$display("%s> RndSeed %d", PortName, RndSeed);
|
||||
// print some info
|
||||
$display("%s> current configuration:", PortName);
|
||||
$display("%s> KillRate %d", PortName, KillRate);
|
||||
$display("%s> FlushRate %d", PortName, FlushRate);
|
||||
$display("%s> TlbHitRate %d", PortName, TlbHitRate);
|
||||
$display("%s> RndSeed %d", PortName, RndSeed);
|
||||
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
`APPL_WAIT_SIG(clk_i,~rst_ni)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
`APPL_WAIT_SIG(clk_i,~rst_ni)
|
||||
|
||||
$display("%s> starting application", PortName);
|
||||
while(~seq_last_i) begin
|
||||
`APPL_WAIT_SIG(clk_i,seq_run_i)
|
||||
unique case(seq_type_i)
|
||||
RANDOM_SEQ: begin
|
||||
$display("%s> start random sequence with %04d responses and req_rate %03d", PortName, seq_num_resp_i, req_rate_i);
|
||||
genRandReq();
|
||||
end
|
||||
LINEAR_SEQ: begin
|
||||
$display("%s> start linear sequence with %04d responses and req_rate %03d", PortName, seq_num_resp_i, req_rate_i);
|
||||
genSeqRead();
|
||||
end
|
||||
WRAP_SEQ: begin
|
||||
$display("%s> start wrapping sequence with %04d responses and req_rate %03d", PortName, seq_num_resp_i, req_rate_i);
|
||||
genWrapSeq();
|
||||
end
|
||||
IDLE_SEQ: begin
|
||||
`APPL_WAIT_SIG(clk_i,seq_end_req)
|
||||
end
|
||||
BURST_SEQ: begin
|
||||
$fatal(1, "Burst sequence not implemented for read port agent");
|
||||
end
|
||||
endcase // seq_type_i
|
||||
seq_end_ack = 1'b1;
|
||||
$display("%s> stop sequence", PortName);
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
seq_end_ack = 1'b0;
|
||||
$display("%s> starting application", PortName);
|
||||
while(~seq_last_i) begin
|
||||
`APPL_WAIT_SIG(clk_i,seq_run_i)
|
||||
unique case(seq_type_i)
|
||||
RANDOM_SEQ: begin
|
||||
$display("%s> start random sequence with %04d responses and req_rate %03d", PortName, seq_num_resp_i, req_rate_i);
|
||||
genRandReq();
|
||||
end
|
||||
$display("%s> ending application", PortName);
|
||||
LINEAR_SEQ: begin
|
||||
$display("%s> start linear sequence with %04d responses and req_rate %03d", PortName, seq_num_resp_i, req_rate_i);
|
||||
genSeqRead();
|
||||
end
|
||||
WRAP_SEQ: begin
|
||||
$display("%s> start wrapping sequence with %04d responses and req_rate %03d", PortName, seq_num_resp_i, req_rate_i);
|
||||
genWrapSeq();
|
||||
end
|
||||
IDLE_SEQ: begin
|
||||
`APPL_WAIT_SIG(clk_i,seq_end_req)
|
||||
end
|
||||
BURST_SEQ: begin
|
||||
$fatal(1, "Burst sequence not implemented for read port agent");
|
||||
end
|
||||
endcase // seq_type_i
|
||||
seq_end_ack = 1'b1;
|
||||
$display("%s> stop sequence", PortName);
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
seq_end_ack = 1'b0;
|
||||
end
|
||||
$display("%s> ending application", PortName);
|
||||
end
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Response acquisition
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
initial begin : p_acq
|
||||
bit ok;
|
||||
progress status;
|
||||
string failingTests, tmpstr1, tmpstr2;
|
||||
int n;
|
||||
logic [63:0] exp_rdata, exp_paddr;
|
||||
logic [1:0] exp_size;
|
||||
initial begin : p_acq
|
||||
bit ok;
|
||||
progress status;
|
||||
string failingTests, tmpstr1, tmpstr2;
|
||||
int n;
|
||||
logic [63:0] exp_rdata, exp_paddr;
|
||||
logic [1:0] exp_size;
|
||||
|
||||
status = new(PortName);
|
||||
failingTests = "";
|
||||
seq_done_o = 1'b0;
|
||||
seq_end_req = 1'b0;
|
||||
prog_end = 1'b0;
|
||||
status = new(PortName);
|
||||
failingTests = "";
|
||||
seq_done_o = 1'b0;
|
||||
seq_end_req = 1'b0;
|
||||
prog_end = 1'b0;
|
||||
|
||||
`ACQ_WAIT_CYC(clk_i,1)
|
||||
`ACQ_WAIT_SIG(clk_i,~rst_ni)
|
||||
`ACQ_WAIT_CYC(clk_i,1)
|
||||
`ACQ_WAIT_SIG(clk_i,~rst_ni)
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// loop over tests
|
||||
n=0;
|
||||
while(~seq_last_i) begin
|
||||
`ACQ_WAIT_SIG(clk_i,seq_run_i)
|
||||
seq_done_o = 1'b0;
|
||||
///////////////////////////////////////////////
|
||||
// loop over tests
|
||||
n=0;
|
||||
while(~seq_last_i) begin
|
||||
`ACQ_WAIT_SIG(clk_i,seq_run_i)
|
||||
seq_done_o = 1'b0;
|
||||
|
||||
$display("%s> %s", PortName, test_name_i);
|
||||
status.reset(seq_num_resp_i);
|
||||
for (int k=0;k<seq_num_resp_i && seq_type_i != IDLE_SEQ;k++) begin
|
||||
`ACQ_WAIT_SIG(clk_i, (dut_req_port_i.data_rvalid & ~dut_req_port_o.kill_req))
|
||||
$display("%s> %s", PortName, test_name_i);
|
||||
status.reset(seq_num_resp_i);
|
||||
for (int k=0;k<seq_num_resp_i && seq_type_i != IDLE_SEQ;k++) begin
|
||||
`ACQ_WAIT_SIG(clk_i, (dut_req_port_i.data_rvalid & ~dut_req_port_o.kill_req))
|
||||
|
||||
exp_rdata = 'x;
|
||||
unique case(exp_size_i)
|
||||
2'b00: exp_rdata[exp_paddr_i[2:0]*8 +: 8] = exp_rdata_i[exp_paddr_i[2:0]*8 +: 8];
|
||||
2'b01: exp_rdata[exp_paddr_i[2:1]*16 +: 16] = exp_rdata_i[exp_paddr_i[2:1]*16 +: 16];
|
||||
2'b10: exp_rdata[exp_paddr_i[2] *32 +: 32] = exp_rdata_i[exp_paddr_i[2] *32 +: 32];
|
||||
2'b11: exp_rdata = exp_rdata_i;
|
||||
endcase // exp_size
|
||||
exp_rdata = 'x;
|
||||
unique case(exp_size_i)
|
||||
2'b00: exp_rdata[exp_paddr_i[2:0]*8 +: 8] = exp_rdata_i[exp_paddr_i[2:0]*8 +: 8];
|
||||
2'b01: exp_rdata[exp_paddr_i[2:1]*16 +: 16] = exp_rdata_i[exp_paddr_i[2:1]*16 +: 16];
|
||||
2'b10: exp_rdata[exp_paddr_i[2] *32 +: 32] = exp_rdata_i[exp_paddr_i[2] *32 +: 32];
|
||||
2'b11: exp_rdata = exp_rdata_i;
|
||||
endcase // exp_size
|
||||
|
||||
// note: wildcard as defined in right operand!
|
||||
ok=(dut_req_port_i.data_rdata ==? exp_rdata) && (exp_paddr_i == act_paddr_i);
|
||||
// note: wildcard as defined in right operand!
|
||||
ok=(dut_req_port_i.data_rdata ==? exp_rdata) && (exp_paddr_i == act_paddr_i);
|
||||
|
||||
if(Verbose | !ok) begin
|
||||
tmpstr1 = $psprintf("vector: %02d - %06d -- exp_paddr: %16X -- exp_data: %16X -- access size: %01d Byte",
|
||||
n, k, exp_paddr_i, exp_rdata, 2**exp_size_i);
|
||||
tmpstr2 = $psprintf("vector: %02d - %06d -- act_paddr: %16X -- act_data: %16X -- access size: %01d Byte",
|
||||
n, k, act_paddr_i, dut_req_port_i.data_rdata, 2**exp_size_i);
|
||||
$display("%s> %s", PortName, tmpstr1);
|
||||
$display("%s> %s", PortName, tmpstr2);
|
||||
end
|
||||
|
||||
if(!ok) begin
|
||||
failingTests = $psprintf("%s%s> %s\n%s> %s\n", failingTests, PortName, tmpstr1, PortName, tmpstr2);
|
||||
end
|
||||
status.addRes(!ok);
|
||||
status.print();
|
||||
end
|
||||
seq_end_req = 1'b1;
|
||||
`ACQ_WAIT_SIG(clk_i, seq_end_ack)
|
||||
seq_end_req = 1'b0;
|
||||
|
||||
`ACQ_WAIT_CYC(clk_i,1)
|
||||
seq_done_o = 1'b1;
|
||||
n++;
|
||||
if(Verbose | !ok) begin
|
||||
tmpstr1 = $psprintf("vector: %02d - %06d -- exp_paddr: %16X -- exp_data: %16X -- access size: %01d Byte",
|
||||
n, k, exp_paddr_i, exp_rdata, 2**exp_size_i);
|
||||
tmpstr2 = $psprintf("vector: %02d - %06d -- act_paddr: %16X -- act_data: %16X -- access size: %01d Byte",
|
||||
n, k, act_paddr_i, dut_req_port_i.data_rdata, 2**exp_size_i);
|
||||
$display("%s> %s", PortName, tmpstr1);
|
||||
$display("%s> %s", PortName, tmpstr2);
|
||||
end
|
||||
///////////////////////////////////////////////
|
||||
|
||||
status.printToFile({PortName, "_summary.rep"}, 1);
|
||||
|
||||
if(status.totErrCnt == 0) begin
|
||||
$display("%s> ----------------------------------------------------------------------", PortName);
|
||||
$display("%s> PASSED %0d VECTORS", PortName, status.totAcqCnt);
|
||||
$display("%s> ----------------------------------------------------------------------\n", PortName);
|
||||
end else begin
|
||||
$display("%s> ----------------------------------------------------------------------\n", PortName);
|
||||
$display("%s> FAILED %0d OF %0d VECTORS\n", PortName , status.totErrCnt, status.totAcqCnt);
|
||||
$display("%s> failing tests:", PortName);
|
||||
$display("%s", failingTests);
|
||||
$display("%s> ----------------------------------------------------------------------\n", PortName);
|
||||
if(!ok) begin
|
||||
failingTests = $psprintf("%s%s> %s\n%s> %s\n", failingTests, PortName, tmpstr1, PortName, tmpstr2);
|
||||
end
|
||||
prog_end = 1'b1;
|
||||
status.addRes(!ok);
|
||||
status.print();
|
||||
end
|
||||
seq_end_req = 1'b1;
|
||||
`ACQ_WAIT_SIG(clk_i, seq_end_ack)
|
||||
seq_end_req = 1'b0;
|
||||
|
||||
`ACQ_WAIT_CYC(clk_i,1)
|
||||
seq_done_o = 1'b1;
|
||||
n++;
|
||||
end
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// assertions
|
||||
///////////////////////////////////////////////////////
|
||||
status.printToFile({PortName, "_summary.rep"}, 1);
|
||||
|
||||
//pragma translate_off
|
||||
// `ifndef VERILATOR
|
||||
|
||||
// `endif
|
||||
//pragma translate_on
|
||||
if(status.totErrCnt == 0) begin
|
||||
$display("%s> ----------------------------------------------------------------------", PortName);
|
||||
$display("%s> PASSED %0d VECTORS", PortName, status.totAcqCnt);
|
||||
$display("%s> ----------------------------------------------------------------------\n", PortName);
|
||||
end else begin
|
||||
$display("%s> ----------------------------------------------------------------------\n", PortName);
|
||||
$display("%s> FAILED %0d OF %0d VECTORS\n", PortName , status.totErrCnt, status.totAcqCnt);
|
||||
$display("%s> failing tests:", PortName);
|
||||
$display("%s", failingTests);
|
||||
$display("%s> ----------------------------------------------------------------------\n", PortName);
|
||||
end
|
||||
prog_end = 1'b1;
|
||||
end
|
||||
|
||||
endprogram // tb_readport
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
// Description: program that emulates a cache write port. the program can generate
|
||||
// randomized or linear read sequences.
|
||||
//
|
||||
//
|
||||
|
||||
`include "tb.svh"
|
||||
|
||||
|
@ -25,276 +21,261 @@ import serpent_cache_pkg::*;
|
|||
import tb_pkg::*;
|
||||
|
||||
program tb_writeport #(
|
||||
parameter string PortName = "write port 0",
|
||||
parameter MemWords = 1024*1024,// in 64bit words
|
||||
parameter logic [63:0] CachedAddrBeg = 0,
|
||||
parameter logic [63:0] CachedAddrEnd = 0,
|
||||
parameter RndSeed = 1110,
|
||||
parameter Verbose = 0
|
||||
parameter string PortName = "write port 0",
|
||||
parameter MemWords = 1024*1024,// in 64bit words
|
||||
parameter logic [63:0] CachedAddrBeg = 0,
|
||||
parameter logic [63:0] CachedAddrEnd = 0,
|
||||
parameter RndSeed = 1110,
|
||||
parameter Verbose = 0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
||||
// to testbench master
|
||||
ref string test_name_i,
|
||||
input logic [6:0] req_rate_i,
|
||||
input seq_t seq_type_i,
|
||||
input logic seq_run_i,
|
||||
input logic [31:0] seq_num_vect_i,
|
||||
input logic seq_last_i,
|
||||
output logic seq_done_o,
|
||||
|
||||
// interface to DUT
|
||||
output dcache_req_i_t dut_req_port_o,
|
||||
input dcache_req_o_t dut_req_port_i
|
||||
);
|
||||
// to testbench master
|
||||
ref string test_name_i,
|
||||
input logic [6:0] req_rate_i,
|
||||
input seq_t seq_type_i,
|
||||
input logic seq_run_i,
|
||||
input logic [31:0] seq_num_vect_i,
|
||||
input logic seq_last_i,
|
||||
output logic seq_done_o,
|
||||
|
||||
// leave this
|
||||
timeunit 1ps;
|
||||
timeprecision 1ps;
|
||||
// interface to DUT
|
||||
output dcache_req_i_t dut_req_port_o,
|
||||
input dcache_req_o_t dut_req_port_i
|
||||
);
|
||||
|
||||
logic [63:0] paddr;
|
||||
|
||||
assign dut_req_port_o.address_tag = paddr[DCACHE_TAG_WIDTH+DCACHE_INDEX_WIDTH-1:DCACHE_INDEX_WIDTH];
|
||||
assign dut_req_port_o.address_index = paddr[DCACHE_INDEX_WIDTH-1:0];
|
||||
assign dut_req_port_o.data_we = dut_req_port_o.data_req;
|
||||
// leave this
|
||||
timeunit 1ps;
|
||||
timeprecision 1ps;
|
||||
|
||||
logic [63:0] paddr;
|
||||
|
||||
assign dut_req_port_o.address_tag = paddr[DCACHE_TAG_WIDTH+DCACHE_INDEX_WIDTH-1:DCACHE_INDEX_WIDTH];
|
||||
assign dut_req_port_o.address_index = paddr[DCACHE_INDEX_WIDTH-1:0];
|
||||
assign dut_req_port_o.data_we = dut_req_port_o.data_req;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helper tasks
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
task automatic applyRandData();
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [7:0] be;
|
||||
automatic logic [1:0] size;
|
||||
task automatic applyRandData();
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [7:0] be;
|
||||
automatic logic [1:0] size;
|
||||
|
||||
void'(randomize(size));
|
||||
// align to size, set correct byte enables
|
||||
be = '0;
|
||||
unique case(size)
|
||||
2'b00: be[paddr[2:0] +: 1] = '1;
|
||||
2'b01: be[paddr[2:1]<<1 +: 2] = '1;
|
||||
2'b10: be[paddr[2:2]<<2 +: 4] = '1;
|
||||
2'b11: be = '1;
|
||||
default: ;
|
||||
endcase
|
||||
paddr[2:0] = '0;
|
||||
|
||||
|
||||
void'(randomize(val));
|
||||
for(int k=0; k<8; k++) begin
|
||||
if( be[k] ) begin
|
||||
dut_req_port_o.data_wdata[k*8 +: 8] = val[k*8 +: 8];
|
||||
end
|
||||
end
|
||||
|
||||
dut_req_port_o.data_be = be;
|
||||
dut_req_port_o.data_size = size;
|
||||
endtask : applyRandData
|
||||
void'(randomize(size));
|
||||
// align to size, set correct byte enables
|
||||
be = '0;
|
||||
unique case(size)
|
||||
2'b00: be[paddr[2:0] +: 1] = '1;
|
||||
2'b01: be[paddr[2:1]<<1 +: 2] = '1;
|
||||
2'b10: be[paddr[2:2]<<2 +: 4] = '1;
|
||||
2'b11: be = '1;
|
||||
default: ;
|
||||
endcase
|
||||
paddr[2:0] = '0;
|
||||
|
||||
|
||||
task automatic genRandReq();
|
||||
automatic logic [63:0] val;
|
||||
void'(randomize(val));
|
||||
for(int k=0; k<8; k++) begin
|
||||
if( be[k] ) begin
|
||||
dut_req_port_o.data_wdata[k*8 +: 8] = val[k*8 +: 8];
|
||||
end
|
||||
end
|
||||
|
||||
void'($urandom(RndSeed));
|
||||
dut_req_port_o.data_be = be;
|
||||
dut_req_port_o.data_size = size;
|
||||
endtask : applyRandData
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
repeat(seq_num_vect_i) begin
|
||||
// randomize request
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address
|
||||
void'(randomize(paddr) with {paddr >= 0; paddr < (MemWords<<3);});
|
||||
applyRandData();
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
task automatic genRandReq();
|
||||
automatic logic [63:0] val;
|
||||
|
||||
void'($urandom(RndSeed));
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
repeat(seq_num_vect_i) begin
|
||||
// randomize request
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address
|
||||
void'(randomize(paddr) with {paddr >= 0; paddr < (MemWords<<3);});
|
||||
applyRandData();
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
endtask : genRandReq
|
||||
|
||||
task automatic genSeqWrite();
|
||||
automatic logic [63:0] val;
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
val = '0;
|
||||
repeat(seq_num_vect_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
dut_req_port_o.data_size = 2'b11;
|
||||
dut_req_port_o.data_be = '1;
|
||||
dut_req_port_o.data_wdata = val;
|
||||
paddr = val;
|
||||
// generate linear read
|
||||
val = (val + 8) % (MemWords<<3);
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
endtask : genSeqWrite
|
||||
|
||||
task automatic genWrapSeq();
|
||||
automatic logic [63:0] val;
|
||||
void'($urandom(RndSeed));
|
||||
|
||||
paddr = CachedAddrBeg;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
val = '0;
|
||||
repeat(seq_num_vect_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
applyRandData();
|
||||
// generate wrapping read of 1 cacheline
|
||||
paddr = CachedAddrBeg + val;
|
||||
val = (val + 8) % (1*(DCACHE_LINE_WIDTH/64)*8);
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
endtask : genWrapSeq
|
||||
|
||||
task automatic genSeqBurst();
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [7:0] be;
|
||||
automatic logic [1:0] size;
|
||||
automatic int cnt, burst_len;
|
||||
|
||||
void'($urandom(RndSeed));
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
cnt = 0;
|
||||
while(cnt < seq_num_vect_i) begin
|
||||
// randomize request
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address base
|
||||
void'(randomize(paddr) with {paddr >= 0; paddr < (MemWords<<3);});
|
||||
|
||||
// do a random burst
|
||||
void'(randomize(burst_len) with {burst_len >= 0; burst_len < 100;});
|
||||
for(int k=0; k<burst_len && cnt < seq_num_vect_i && paddr < ((MemWords-1)<<3); k++) begin
|
||||
applyRandData();
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
//void'(randomize(val) with {val>=0 val<=8;};);
|
||||
paddr += 8;
|
||||
cnt ++;
|
||||
end
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
endtask : genRandReq
|
||||
|
||||
task automatic genSeqWrite();
|
||||
automatic logic [63:0] val;
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
val = '0;
|
||||
repeat(seq_num_vect_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
dut_req_port_o.data_size = 2'b11;
|
||||
dut_req_port_o.data_be = '1;
|
||||
dut_req_port_o.data_wdata = val;
|
||||
paddr = val;
|
||||
// generate linear read
|
||||
val = (val + 8) % (MemWords<<3);
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
endtask : genSeqWrite
|
||||
|
||||
task automatic genWrapSeq();
|
||||
automatic logic [63:0] val;
|
||||
void'($urandom(RndSeed));
|
||||
|
||||
paddr = CachedAddrBeg;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
val = '0;
|
||||
repeat(seq_num_vect_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
applyRandData();
|
||||
// generate wrapping read of 1 cacheline
|
||||
paddr = CachedAddrBeg + val;
|
||||
val = (val + 8) % (1*(DCACHE_LINE_WIDTH/64)*8);
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
endtask : genWrapSeq
|
||||
|
||||
task automatic genSeqBurst();
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [7:0] be;
|
||||
automatic logic [1:0] size;
|
||||
automatic int cnt, burst_len;
|
||||
|
||||
void'($urandom(RndSeed));
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
cnt = 0;
|
||||
while(cnt < seq_num_vect_i) begin
|
||||
// randomize request
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address base
|
||||
void'(randomize(paddr) with {paddr >= 0; paddr < (MemWords<<3);});
|
||||
|
||||
// do a random burst
|
||||
void'(randomize(burst_len) with {burst_len >= 0; burst_len < 100;});
|
||||
for(int k=0; k<burst_len && cnt < seq_num_vect_i && paddr < ((MemWords-1)<<3); k++) begin
|
||||
applyRandData();
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
//void'(randomize(val) with {val>=0 val<=8;};);
|
||||
paddr += 8;
|
||||
cnt ++;
|
||||
end
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
|
||||
endtask : genSeqBurst
|
||||
paddr = '0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = 'x;
|
||||
endtask : genSeqBurst
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Sequence application
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
initial begin : p_stim
|
||||
paddr = '0;
|
||||
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = '0;
|
||||
dut_req_port_o.tag_valid = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
initial begin : p_stim
|
||||
paddr = '0;
|
||||
|
||||
seq_done_o = 1'b0;
|
||||
dut_req_port_o.data_req = '0;
|
||||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.data_be = '0;
|
||||
dut_req_port_o.data_wdata = '0;
|
||||
dut_req_port_o.tag_valid = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
|
||||
// print some info
|
||||
$display("%s> current configuration:", PortName);
|
||||
$display("%s> RndSeed %d", PortName, RndSeed);
|
||||
seq_done_o = 1'b0;
|
||||
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
`APPL_WAIT_SIG(clk_i,~rst_ni)
|
||||
|
||||
$display("%s> starting application", PortName);
|
||||
while(~seq_last_i) begin
|
||||
`APPL_WAIT_SIG(clk_i,seq_run_i)
|
||||
seq_done_o = 1'b0;
|
||||
unique case(seq_type_i)
|
||||
RANDOM_SEQ: begin
|
||||
$display("%s> start random sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genRandReq();
|
||||
end
|
||||
LINEAR_SEQ: begin
|
||||
$display("%s> start linear sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genSeqWrite();
|
||||
end
|
||||
WRAP_SEQ: begin
|
||||
$display("%s> start wrapping sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genWrapSeq();
|
||||
end
|
||||
IDLE_SEQ: ;// do nothing
|
||||
BURST_SEQ: begin
|
||||
$display("%s> start burst sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genSeqBurst();
|
||||
end
|
||||
endcase // seq_type_i
|
||||
seq_done_o = 1'b1;
|
||||
$display("%s> stop sequence", PortName);
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
// print some info
|
||||
$display("%s> current configuration:", PortName);
|
||||
$display("%s> RndSeed %d", PortName, RndSeed);
|
||||
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
`APPL_WAIT_SIG(clk_i,~rst_ni)
|
||||
|
||||
$display("%s> starting application", PortName);
|
||||
while(~seq_last_i) begin
|
||||
`APPL_WAIT_SIG(clk_i,seq_run_i)
|
||||
seq_done_o = 1'b0;
|
||||
unique case(seq_type_i)
|
||||
RANDOM_SEQ: begin
|
||||
$display("%s> start random sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genRandReq();
|
||||
end
|
||||
$display("%s> ending application", PortName);
|
||||
LINEAR_SEQ: begin
|
||||
$display("%s> start linear sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genSeqWrite();
|
||||
end
|
||||
WRAP_SEQ: begin
|
||||
$display("%s> start wrapping sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genWrapSeq();
|
||||
end
|
||||
IDLE_SEQ: ;// do nothing
|
||||
BURST_SEQ: begin
|
||||
$display("%s> start burst sequence with %04d vectors and req_rate %03d", PortName, seq_num_vect_i, req_rate_i);
|
||||
genSeqBurst();
|
||||
end
|
||||
endcase // seq_type_i
|
||||
seq_done_o = 1'b1;
|
||||
$display("%s> stop sequence", PortName);
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// assertions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
//pragma translate_off
|
||||
// `ifndef verilator
|
||||
// exp_resp_vld: assert property (
|
||||
// @(posedge clk_i) disable iff (~rst_ni) dut_req_port_i.data_rvalid |-> exp_rdata_queue.size()>0 && exp_size_queue.size()>0 && exp_paddr_queue.size()>0)
|
||||
// else $fatal(1, "expected response must be in the queue when DUT response returns");
|
||||
|
||||
// `endif
|
||||
//pragma translate_on
|
||||
$display("%s> ending application", PortName);
|
||||
end
|
||||
|
||||
endprogram // tb_readport
|
||||
|
|
6
tb/tb_serpent_icache/.gitignore
vendored
6
tb/tb_serpent_icache/.gitignore
vendored
|
@ -1,3 +1,7 @@
|
|||
work
|
||||
modelsim.ini
|
||||
*.rep
|
||||
transcript
|
||||
*.ini
|
||||
*.wlf
|
||||
*.log
|
||||
|
||||
|
|
|
@ -1,23 +1,40 @@
|
|||
# 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
# Date: 15.08.2018
|
||||
# Description: Makefile for the icache testbench.
|
||||
|
||||
|
||||
library ?= work
|
||||
toplevel ?= tb
|
||||
src-list := tb.list
|
||||
src := $(shell xargs printf '\n%s' < $(src-list) | cut -b 1-)
|
||||
compile_flag += +cover+/dut -incr -64 -nologo
|
||||
compile_flag += +cover+/dut -incr -64 -nologo
|
||||
sim_opts += -64 -coverage -classdebug -voptargs="+acc"
|
||||
questa_version ?= ${QUESTASIM_VERSION}
|
||||
incdir += ../common/
|
||||
|
||||
build: clean
|
||||
vlib${questa_version} $(library)
|
||||
vlog${questa_version} -work $(library) -pedanticerrors $(src) $(compile_flag)
|
||||
vlog${questa_version} -work $(library) -pedanticerrors $(src) $(compile_flag) +incdir+$(incdir)
|
||||
touch $(library)/.build
|
||||
|
||||
# this starts modelsim with gui
|
||||
sim: build
|
||||
vsim${questa_version} -lib $(library) $(toplevel) -do "do wave.do" $(sim_opts)
|
||||
|
||||
# batch mode without gui
|
||||
simc: build
|
||||
vsim${questa_version} -lib $(library) $(toplevel) -c -do "run -all; exit" $(sim_opts)
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf $(library)
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
// Description: testbench for piton_icache. includes the following tests:
|
||||
//
|
||||
//
|
||||
// 0) random accesses with disabled cache
|
||||
// 1) random accesses with enabled cache to cacheable and noncacheable memory
|
||||
// 2) linear, wrapping sweep with enabled cache
|
||||
// 3) 1) with random stalls on the memory side and TLB side
|
||||
// 3) 1) with random stalls on the memory side and TLB side
|
||||
// 4) nr 3) with random invalidations
|
||||
//
|
||||
//
|
||||
// note that we use a simplified address translation scheme to emulate the TLB.
|
||||
// (random offsets).
|
||||
|
||||
|
@ -29,8 +25,10 @@ import ariane_pkg::*;
|
|||
import serpent_cache_pkg::*;
|
||||
import tb_pkg::*;
|
||||
|
||||
`include "tb.svh"
|
||||
|
||||
module tb;
|
||||
|
||||
|
||||
// leave this
|
||||
timeunit 1ps;
|
||||
timeprecision 1ps;
|
||||
|
@ -40,7 +38,7 @@ module tb;
|
|||
parameter MemWords = MemBytes>>2;
|
||||
parameter logic [63:0] CachedAddrBeg = MemBytes/4;
|
||||
parameter logic [63:0] CachedAddrEnd = 64'hFFFF_FFFF_FFFF_FFFF;
|
||||
|
||||
|
||||
// rates are in percent
|
||||
parameter TlbRandHitRate = 50;
|
||||
parameter MemRandHitRate = 50;
|
||||
|
@ -95,22 +93,22 @@ module tb;
|
|||
logic exp_empty, exp_pop;
|
||||
|
||||
logic dut_out_vld, dut_in_rdy;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Clock Process
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
always @*
|
||||
begin
|
||||
do begin
|
||||
clk_i = 1;#(CLK_HI);
|
||||
clk_i = 0;#(CLK_LO);
|
||||
clk_i = 1;#(CLK_HI);
|
||||
clk_i = 0;#(CLK_LO);
|
||||
end while (end_of_sim == 1'b0);
|
||||
repeat (100) begin
|
||||
// generate a few extra cycle to allow response acquisition to complete
|
||||
clk_i = 1;#(CLK_HI);
|
||||
clk_i = 0;#(CLK_LO);
|
||||
end
|
||||
// generate a few extra cycle to allow response acquisition to complete
|
||||
clk_i = 1;#(CLK_HI);
|
||||
clk_i = 0;#(CLK_LO);
|
||||
end
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -129,23 +127,23 @@ module tb;
|
|||
stim_end = 0;
|
||||
|
||||
stim_start = 1;
|
||||
applWaitCyc(clk_i,10);
|
||||
`APPL_WAIT_CYC(clk_i,10)
|
||||
stim_start = 0;
|
||||
|
||||
|
||||
// start with clean cache
|
||||
flush_i = 1;
|
||||
applWaitCyc(clk_i,1);
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
flush_i = 0;
|
||||
|
||||
while(~acq_done) begin
|
||||
// randomize request
|
||||
dreq_i.req = 0;
|
||||
ok = randomize(val) with {val > 0; val <= 100;};
|
||||
if (val < SeqRate) begin
|
||||
if (val < SeqRate) begin
|
||||
dreq_i.req = 1;
|
||||
// generate random address
|
||||
ok = randomize(val) with {val >= 0; val < (MemBytes-TlbOffset)>>2;};
|
||||
dreq_i.vaddr = val<<2;// align to 4Byte
|
||||
dreq_i.vaddr = val<<2;// align to 4Byte
|
||||
// generate random control events
|
||||
ok = randomize(val) with {val > 0; val <= 100;};
|
||||
dreq_i.kill_s1 = (val < S1KillRate);
|
||||
|
@ -153,11 +151,11 @@ module tb;
|
|||
dreq_i.kill_s2 = (val < S2KillRate);
|
||||
ok = randomize(val) with {val > 0; val <= 100;};
|
||||
flush_i = (val < FlushRate);
|
||||
applWait(clk_i, dut_in_rdy);
|
||||
`APPL_WAIT_SIG(clk_i, dut_in_rdy)
|
||||
end else begin
|
||||
applWaitCyc(clk_i,1);
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
end
|
||||
end
|
||||
stim_end = 1;
|
||||
endtask : genRandReq
|
||||
|
||||
|
@ -174,12 +172,12 @@ module tb;
|
|||
stim_end = 0;
|
||||
|
||||
stim_start = 1;
|
||||
applWaitCyc(clk_i,10);
|
||||
`APPL_WAIT_CYC(clk_i,10)
|
||||
stim_start = 0;
|
||||
|
||||
// start with clean cache
|
||||
flush_i = 1;
|
||||
applWaitCyc(clk_i,1);
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
flush_i = 0;
|
||||
|
||||
while(~acq_done) begin
|
||||
|
@ -187,8 +185,8 @@ module tb;
|
|||
dreq_i.vaddr = addr;
|
||||
// generate linear read
|
||||
addr = (addr + 4) % (MemBytes - TlbOffset);
|
||||
applWait(clk_i, dut_in_rdy);
|
||||
end
|
||||
`APPL_WAIT_SIG(clk_i, dut_in_rdy)
|
||||
end
|
||||
stim_end = 1;
|
||||
endtask : genSeqRead
|
||||
|
||||
|
@ -196,10 +194,10 @@ module tb;
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TLB and memory emulation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
tlb_emul #(
|
||||
|
||||
tlb_emul #(
|
||||
.TlbRandHitRate(TlbRandHitRate)
|
||||
) i_tlb_emul (
|
||||
) i_tlb_emul (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.tlb_rand_en_i ( tlb_rand_en ),
|
||||
|
@ -208,87 +206,87 @@ tlb_emul #(
|
|||
// icache interface
|
||||
.req_i ( areq_o ),
|
||||
.req_o ( areq_i )
|
||||
);
|
||||
);
|
||||
|
||||
mem_emul #(
|
||||
.MemRandHitRate ( MemRandHitRate ),
|
||||
.MemRandInvRate ( MemRandInvRate ),
|
||||
.MemWords ( MemWords ),
|
||||
.CachedAddrBeg ( CachedAddrBeg )
|
||||
) i_mem_emul (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.mem_rand_en_i ( mem_rand_en ),
|
||||
.io_rand_en_i ( io_rand_en ),
|
||||
.inv_rand_en_i ( inv_rand_en ),
|
||||
.tlb_offset_i ( TlbOffset ),
|
||||
.stim_vaddr_i ( stim_vaddr ),
|
||||
.stim_push_i ( stim_push ),
|
||||
.stim_flush_i ( stim_flush ),
|
||||
.stim_full_o ( stim_full ),
|
||||
.exp_data_o ( exp_data ),
|
||||
.exp_vaddr_o ( exp_vaddr ),
|
||||
.exp_empty_o ( exp_empty ),
|
||||
.exp_pop_i ( exp_pop ),
|
||||
.mem_data_req_i ( mem_data_req_o ),
|
||||
.mem_data_ack_o ( mem_data_ack_i ),
|
||||
.mem_data_i ( mem_data_o ),
|
||||
.mem_rtrn_vld_o ( mem_rtrn_vld_i ),
|
||||
.mem_rtrn_o ( mem_rtrn_i )
|
||||
);
|
||||
mem_emul #(
|
||||
.MemRandHitRate ( MemRandHitRate ),
|
||||
.MemRandInvRate ( MemRandInvRate ),
|
||||
.MemWords ( MemWords ),
|
||||
.CachedAddrBeg ( CachedAddrBeg )
|
||||
) i_mem_emul (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.mem_rand_en_i ( mem_rand_en ),
|
||||
.io_rand_en_i ( io_rand_en ),
|
||||
.inv_rand_en_i ( inv_rand_en ),
|
||||
.tlb_offset_i ( TlbOffset ),
|
||||
.stim_vaddr_i ( stim_vaddr ),
|
||||
.stim_push_i ( stim_push ),
|
||||
.stim_flush_i ( stim_flush ),
|
||||
.stim_full_o ( stim_full ),
|
||||
.exp_data_o ( exp_data ),
|
||||
.exp_vaddr_o ( exp_vaddr ),
|
||||
.exp_empty_o ( exp_empty ),
|
||||
.exp_pop_i ( exp_pop ),
|
||||
.mem_data_req_i ( mem_data_req_o ),
|
||||
.mem_data_ack_o ( mem_data_ack_i ),
|
||||
.mem_data_i ( mem_data_o ),
|
||||
.mem_rtrn_vld_o ( mem_rtrn_vld_i ),
|
||||
.mem_rtrn_o ( mem_rtrn_i )
|
||||
);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MUT
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
serpent_icache #(
|
||||
.CachedAddrBeg(CachedAddrBeg),
|
||||
.CachedAddrEnd(CachedAddrEnd)
|
||||
) dut (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.flush_i ( flush_i ),
|
||||
.en_i ( en_i ),
|
||||
.miss_o ( miss_o ),
|
||||
.areq_i ( areq_i ),
|
||||
.areq_o ( areq_o ),
|
||||
.dreq_i ( dreq_i ),
|
||||
.dreq_o ( dreq_o ),
|
||||
.mem_rtrn_vld_i ( mem_rtrn_vld_i ),
|
||||
.mem_rtrn_i ( mem_rtrn_i ),
|
||||
.mem_data_req_o ( mem_data_req_o ),
|
||||
.mem_data_ack_i ( mem_data_ack_i ),
|
||||
.mem_data_o ( mem_data_o )
|
||||
);
|
||||
serpent_icache #(
|
||||
.CachedAddrBeg(CachedAddrBeg),
|
||||
.CachedAddrEnd(CachedAddrEnd)
|
||||
) dut (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.flush_i ( flush_i ),
|
||||
.en_i ( en_i ),
|
||||
.miss_o ( miss_o ),
|
||||
.areq_i ( areq_i ),
|
||||
.areq_o ( areq_o ),
|
||||
.dreq_i ( dreq_i ),
|
||||
.dreq_o ( dreq_o ),
|
||||
.mem_rtrn_vld_i ( mem_rtrn_vld_i ),
|
||||
.mem_rtrn_i ( mem_rtrn_i ),
|
||||
.mem_data_req_o ( mem_data_req_o ),
|
||||
.mem_data_ack_i ( mem_data_ack_i ),
|
||||
.mem_data_o ( mem_data_o )
|
||||
);
|
||||
|
||||
// connect interface to expected response channel of memory emulation
|
||||
assign stim_vaddr = dreq_i.vaddr;
|
||||
assign stim_push = dreq_i.req & dreq_o.ready & (~dreq_i.kill_s1) & (~flush_i);
|
||||
assign stim_flush = 0;
|
||||
assign exp_pop = (dreq_o.valid | dreq_i.kill_s2) & (~exp_empty);
|
||||
// connect interface to expected response channel of memory emulation
|
||||
assign stim_vaddr = dreq_i.vaddr;
|
||||
assign stim_push = dreq_i.req & dreq_o.ready & (~dreq_i.kill_s1) & (~flush_i);
|
||||
assign stim_flush = 0;
|
||||
assign exp_pop = (dreq_o.valid | dreq_i.kill_s2) & (~exp_empty);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// stimuli application process
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
assign dut_in_rdy = dreq_o.ready;
|
||||
|
||||
initial // process runs just once
|
||||
begin : p_stim
|
||||
end_of_sim = 0;
|
||||
begin : p_stim
|
||||
end_of_sim = 0;
|
||||
num_vectors = 0;
|
||||
stim_start = 0;
|
||||
stim_end = 0;
|
||||
|
||||
|
||||
rst_ni = 0;
|
||||
|
||||
|
||||
mem_rand_en = 0;
|
||||
tlb_rand_en = 0;
|
||||
inv_rand_en = 0;
|
||||
exception_en = 0;
|
||||
io_rand_en = 0;
|
||||
|
||||
dreq_i.req = 0;
|
||||
dreq_i.req = 0;
|
||||
dreq_i.kill_s1 = 0;
|
||||
dreq_i.kill_s2 = 0;
|
||||
dreq_i.vaddr = 0;
|
||||
|
@ -305,35 +303,35 @@ assign exp_pop = (dreq_o.valid | dreq_i.kill_s2) & (~exp_empty);
|
|||
$display("TB> S1KillRate %d", S1KillRate);
|
||||
$display("TB> S2KillRate %d", S2KillRate);
|
||||
$display("TB> FlushRate %d", FlushRate);
|
||||
|
||||
applWaitCyc(clk_i,100);
|
||||
|
||||
`APPL_WAIT_CYC(clk_i,100);
|
||||
$display("TB> choose TLB offset %16X", TlbOffset);
|
||||
|
||||
// reset cycles
|
||||
applWaitCyc(clk_i,100);
|
||||
rst_ni = 1'b1;
|
||||
applWaitCyc(clk_i,100);
|
||||
|
||||
$display("TB> stimuli application started");
|
||||
// apply each test until NUM_ACCESSES memory
|
||||
`APPL_WAIT_CYC(clk_i,100);
|
||||
rst_ni = 1'b1;
|
||||
`APPL_WAIT_CYC(clk_i,100);
|
||||
|
||||
$display("TB> stimuli application started");
|
||||
// apply each test until NUM_ACCESSES memory
|
||||
// requests have successfully completed
|
||||
///////////////////////////////////////////////
|
||||
// TEST 0
|
||||
en_i = 0;
|
||||
genRandReq();
|
||||
applWaitCyc(clk_i,40);
|
||||
`APPL_WAIT_CYC(clk_i,40);
|
||||
///////////////////////////////////////////////
|
||||
// TEST 1
|
||||
test_name = "TEST1, enabled cache";
|
||||
en_i = 1;
|
||||
genRandReq();
|
||||
applWaitCyc(clk_i,40);
|
||||
`APPL_WAIT_CYC(clk_i,40);
|
||||
///////////////////////////////////////////////
|
||||
// TEST 2
|
||||
test_name = "TEST2, enabled cache, sequential reads";
|
||||
en_i = 1;
|
||||
genSeqRead();
|
||||
applWaitCyc(clk_i,40);
|
||||
`APPL_WAIT_CYC(clk_i,40);
|
||||
///////////////////////////////////////////////
|
||||
// TEST 3
|
||||
test_name = "TEST3, enabled cache, random stalls in mem and TLB side";
|
||||
|
@ -341,7 +339,7 @@ assign exp_pop = (dreq_o.valid | dreq_i.kill_s2) & (~exp_empty);
|
|||
mem_rand_en = 1;
|
||||
tlb_rand_en = 1;
|
||||
genRandReq();
|
||||
applWaitCyc(clk_i,40);
|
||||
`APPL_WAIT_CYC(clk_i,40);
|
||||
///////////////////////////////////////////////
|
||||
// TEST 4
|
||||
test_name = "TEST4, +random invalidations";
|
||||
|
@ -350,20 +348,20 @@ assign exp_pop = (dreq_o.valid | dreq_i.kill_s2) & (~exp_empty);
|
|||
tlb_rand_en = 1;
|
||||
inv_rand_en = 1;
|
||||
genRandReq();
|
||||
applWaitCyc(clk_i,40);
|
||||
`APPL_WAIT_CYC(clk_i,40);
|
||||
///////////////////////////////////////////////
|
||||
end_of_sim = 1;
|
||||
$display("TB> stimuli application ended");
|
||||
end
|
||||
$display("TB> stimuli application ended");
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// stimuli acquisition process
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
assign dut_out_vld = dreq_o.valid;
|
||||
|
||||
initial // process runs just once
|
||||
begin : p_acq
|
||||
begin : p_acq
|
||||
|
||||
bit ok;
|
||||
progress status;
|
||||
|
@ -379,39 +377,39 @@ assign exp_pop = (dreq_o.valid | dreq_i.kill_s2) & (~exp_empty);
|
|||
n=0;
|
||||
while (~end_of_sim) begin
|
||||
// wait for stimuli application
|
||||
acqWait(clk_i, stim_start);
|
||||
`ACQ_WAIT_SIG(clk_i, stim_start);
|
||||
$display("TB: ----------------------------------------------------------------------\n");
|
||||
$display("TB> %s", test_name);
|
||||
|
||||
$display("TB> %s", test_name);
|
||||
|
||||
status.reset(num_vectors);
|
||||
acq_done = 0;
|
||||
for (int k=0;k<num_vectors;k++) begin
|
||||
|
||||
// wait for response
|
||||
acqWait(clk_i, dut_out_vld);
|
||||
|
||||
|
||||
// wait for response
|
||||
`ACQ_WAIT_SIG(clk_i, dut_out_vld);
|
||||
|
||||
ok=(dreq_o.data == exp_data[FETCH_WIDTH-1:0]) && (dreq_o.vaddr == exp_vaddr);
|
||||
|
||||
|
||||
if(!ok) begin
|
||||
tmpstr =
|
||||
tmpstr =
|
||||
$psprintf("vector: %02d - %06d -- exp_vaddr: %16X -- act_vaddr : %16X -- exp_data: %08X -- act_data: %08X",
|
||||
n, k, exp_vaddr, dreq_o.vaddr, exp_data[FETCH_WIDTH-1:0], dreq_o.data);
|
||||
failingTests = $psprintf("%sTB: %s\n", failingTests, tmpstr);
|
||||
$display("TB> %s", tmpstr);
|
||||
end
|
||||
end
|
||||
status.addRes(!ok);
|
||||
status.print();
|
||||
end
|
||||
acq_done = 1;
|
||||
n++;
|
||||
// wait for stimuli application end
|
||||
acqWait(clk_i, stim_end);
|
||||
acqWaitCyc(clk_i,100);
|
||||
end
|
||||
`ACQ_WAIT_SIG(clk_i, stim_end);
|
||||
`ACQ_WAIT_CYC(clk_i,100);
|
||||
end
|
||||
///////////////////////////////////////////////
|
||||
|
||||
status.printToFile("summary.rep", 1);
|
||||
|
||||
|
||||
status.printToFile("summary.rep", 1);
|
||||
|
||||
if(status.totErrCnt == 0) begin
|
||||
$display("TB: ----------------------------------------------------------------------\n");
|
||||
$display("TB: PASSED %0d VECTORS", status.totAcqCnt);
|
||||
|
@ -423,12 +421,12 @@ assign exp_pop = (dreq_o.valid | dreq_i.kill_s2) & (~exp_empty);
|
|||
$display("%s", failingTests);
|
||||
$display("TB: ----------------------------------------------------------------------\n");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
@ -20,7 +16,7 @@
|
|||
package tb_pkg;
|
||||
|
||||
// // for abs(double) function
|
||||
// import mti_cstdlib::*;
|
||||
// import mti_cstdlib::*;
|
||||
|
||||
// for timestamps
|
||||
import "DPI-C" \time = function int _time (inout int tloc[4]);
|
||||
|
@ -29,53 +25,19 @@ package tb_pkg;
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// parameters
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// creates a 10ns ATI timing cycle
|
||||
time CLK_HI = 5ns; // set clock high time
|
||||
time CLK_LO = 5ns; // set clock low time
|
||||
time APPL_DEL = 2ns; // set stimuli application delay
|
||||
time ACQ_DEL = 8ns; // set response aquisition delay
|
||||
|
||||
// creates a 10ns ATI timing cycle
|
||||
time CLK_HI = 5ns; // set clock high time
|
||||
time CLK_LO = 5ns; // set clock low time
|
||||
time APPL_DEL = 2ns; // set stimuli application delay
|
||||
time ACQ_DEL = 8ns; // set response aquisition delay
|
||||
|
||||
parameter ERROR_CNT_STOP_LEVEL = 1; // use 1 for debugging. 0 runs the complete simulation...
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// use to ensure proper ATI timing
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// progress
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
task automatic applWaitCyc(ref logic Clk_C, input int unsigned n);
|
||||
if (n > 0) begin
|
||||
repeat (n) @(posedge(Clk_C));
|
||||
#(APPL_DEL);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic acqWaitCyc(ref logic Clk_C, input int unsigned n);
|
||||
if (n > 0) begin
|
||||
repeat (n) @(posedge(Clk_C));
|
||||
#(ACQ_DEL);
|
||||
end
|
||||
endtask
|
||||
|
||||
// sample right on active clock edge
|
||||
task automatic applWait(ref logic Clk_C, ref logic SigToWaitFor_S);
|
||||
do begin
|
||||
@(posedge(Clk_C));
|
||||
end while(SigToWaitFor_S == 1'b0);
|
||||
#(APPL_DEL);
|
||||
endtask
|
||||
|
||||
// sample right on active clock edge
|
||||
task automatic acqWait(ref logic Clk_C, ref logic SigToWaitFor_S);
|
||||
do begin
|
||||
@(posedge(Clk_C));
|
||||
end while(SigToWaitFor_S == 1'b0);
|
||||
//#(ACQ_DEL);
|
||||
endtask
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// progress
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class progress;
|
||||
real newState, oldState;
|
||||
longint numResp, acqCnt, errCnt, totAcqCnt, totErrCnt;
|
||||
|
@ -90,20 +52,20 @@ package tb_pkg;
|
|||
this.totAcqCnt = 0;
|
||||
this.totErrCnt = 0;
|
||||
|
||||
end
|
||||
end
|
||||
endfunction : new
|
||||
|
||||
|
||||
function void reset(longint numResp_);
|
||||
begin
|
||||
this.acqCnt = 0;
|
||||
this.errCnt = 0;
|
||||
this.newState = 0.0;
|
||||
this.oldState = 0.0;
|
||||
this.numResp = numResp_;
|
||||
end
|
||||
this.numResp = numResp_;
|
||||
end
|
||||
endfunction : reset
|
||||
|
||||
function void addRes(int isError);
|
||||
function void addRes(int isError);
|
||||
begin
|
||||
this.acqCnt++;
|
||||
this.totAcqCnt++;
|
||||
|
@ -111,16 +73,16 @@ package tb_pkg;
|
|||
this.totErrCnt += isError;
|
||||
|
||||
if(ERROR_CNT_STOP_LEVEL <= this.errCnt && ERROR_CNT_STOP_LEVEL > 0) begin
|
||||
$error("TB> simulation stopped (ERROR_CNT_STOP_LEVEL = %d reached).", ERROR_CNT_STOP_LEVEL);
|
||||
$error("TB> simulation stopped (ERROR_CNT_STOP_LEVEL = %d reached).", ERROR_CNT_STOP_LEVEL);
|
||||
$stop();
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
endfunction : addRes
|
||||
|
||||
function void print();
|
||||
function void print();
|
||||
begin
|
||||
this.newState = $itor(this.acqCnt) / $itor(this.numResp);
|
||||
if(this.newState - this.oldState >= 0.01) begin
|
||||
if(this.newState - this.oldState >= 0.01) begin
|
||||
$display("TB> validated %03d%% -- %01d failed (%03.3f%%) ",
|
||||
$rtoi(this.newState*100.0),
|
||||
this.errCnt,
|
||||
|
@ -131,10 +93,10 @@ package tb_pkg;
|
|||
end
|
||||
endfunction : print
|
||||
|
||||
function void printToFile(string file, bit summary = 0);
|
||||
function void printToFile(string file, bit summary = 0);
|
||||
begin
|
||||
int fptr;
|
||||
|
||||
|
||||
// sanitize string
|
||||
for(fptr=0; fptr<$size(file);fptr++) begin
|
||||
if(file[fptr] == " " || file[fptr] == "/" || file[fptr] == "\\") begin
|
||||
|
@ -155,13 +117,13 @@ package tb_pkg;
|
|||
end else begin
|
||||
$fdisplay(fptr, "CI: FAILED");
|
||||
end
|
||||
end else begin
|
||||
end else begin
|
||||
$fdisplay(fptr, "test name: %s", file);
|
||||
$fdisplay(fptr, "this test: %01d of %01d vectors failed (%03.3f%%) ",
|
||||
this.errCnt,
|
||||
this.acqCnt,
|
||||
$itor(this.errCnt) / $itor(this.acqCnt) * 100.0);
|
||||
|
||||
|
||||
$fdisplay(fptr, "total so far: %01d of %01d vectors failed (%03.3f%%) ",
|
||||
this.totErrCnt,
|
||||
this.totAcqCnt,
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Copyright (c) 2018 ETH Zurich, University of Bologna
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code is under development and not yet released to the public.
|
||||
// Until it is released, the code is under the copyright of ETH Zurich and
|
||||
// the University of Bologna, and may contain confidential and/or unpublished
|
||||
// work. Any reuse/redistribution is strictly forbidden without written
|
||||
// permission from ETH Zurich.
|
||||
//
|
||||
// Bug fixes and contributions will eventually be released under the
|
||||
// SolderPad open hardware license in the context of the PULP platform
|
||||
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
|
||||
// University of Bologna.
|
||||
// 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 15.08.2018
|
||||
|
@ -21,55 +17,54 @@ import ariane_pkg::*;
|
|||
import serpent_cache_pkg::*;
|
||||
|
||||
module tlb_emul #(
|
||||
parameter TlbRandHitRate = 10 //in percent
|
||||
)(
|
||||
parameter TlbRandHitRate = 10 //in percent
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
||||
input logic tlb_rand_en_i,
|
||||
input logic exception_en_i,
|
||||
input logic [63:0] tlb_offset_i,
|
||||
|
||||
// icache interface
|
||||
input icache_areq_o_t req_i,
|
||||
output icache_areq_i_t req_o
|
||||
input logic tlb_rand_en_i,
|
||||
input logic exception_en_i,
|
||||
input logic [63:0] tlb_offset_i,
|
||||
|
||||
// icache interface
|
||||
input icache_areq_o_t req_i,
|
||||
output icache_areq_i_t req_o
|
||||
);
|
||||
|
||||
logic tlb_ready_d, tlb_ready_q;
|
||||
|
||||
logic tlb_ready_d, tlb_ready_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : p_tlb_rand
|
||||
automatic bit ok = 0;
|
||||
automatic int rnd = 0;
|
||||
automatic bit ok = 0;
|
||||
automatic int rnd = 0;
|
||||
|
||||
assert(TlbRandHitRate<=100 && TlbRandHitRate>=0) else
|
||||
$fatal("TlbRandHitRate must be a percentage");
|
||||
assert(TlbRandHitRate<=100 && TlbRandHitRate>=0) else
|
||||
$fatal("TlbRandHitRate must be a percentage");
|
||||
|
||||
if(~rst_ni) begin
|
||||
tlb_ready_q <= '0;
|
||||
if(~rst_ni) begin
|
||||
tlb_ready_q <= '0;
|
||||
end else begin
|
||||
if (tlb_rand_en_i) begin
|
||||
ok = randomize(rnd) with {rnd > 0; rnd <= 100;};
|
||||
if(rnd < TlbRandHitRate) begin
|
||||
tlb_ready_q = '1;
|
||||
end else
|
||||
tlb_ready_q = '0;
|
||||
end else begin
|
||||
if (tlb_rand_en_i) begin
|
||||
ok = randomize(rnd) with {rnd > 0; rnd <= 100;};
|
||||
if(rnd < TlbRandHitRate) begin
|
||||
tlb_ready_q = '1;
|
||||
end else
|
||||
tlb_ready_q = '0;
|
||||
end else begin
|
||||
tlb_ready_q = '1;
|
||||
end
|
||||
tlb_ready_q = '1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// TODO: add random exceptions
|
||||
always_comb begin : proc_tlb
|
||||
req_o.fetch_valid = '0;
|
||||
req_o.fetch_paddr = '0;
|
||||
req_o.fetch_exception = '0;
|
||||
req_o.fetch_valid = '0;
|
||||
req_o.fetch_paddr = '0;
|
||||
req_o.fetch_exception = '0;
|
||||
|
||||
if (req_i.fetch_req && tlb_ready_q) begin
|
||||
req_o.fetch_valid = 1'b1;
|
||||
req_o.fetch_paddr = req_i.fetch_vaddr + tlb_offset_i;
|
||||
end
|
||||
if (req_i.fetch_req && tlb_ready_q) begin
|
||||
req_o.fetch_valid = 1'b1;
|
||||
req_o.fetch_paddr = req_i.fetch_vaddr + tlb_offset_i;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // tlb_emul
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue