Merge branch 'ariane_next' of github.com:pulp-platform/ariane into ariane_next

This commit is contained in:
Florian Zaruba 2018-11-27 16:40:26 +01:00
commit 3635ddcd02
No known key found for this signature in database
GPG key ID: E742FFE8EC38A792
24 changed files with 1981 additions and 2083 deletions

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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
View file

@ -0,0 +1,7 @@
work
*.rep
transcript
*.ini
*.wlf
*.log

View file

@ -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)

View file

@ -1,3 +1,7 @@
work
modelsim.ini
*.rep
transcript
*.ini
*.wlf
*.log

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -1,3 +1,7 @@
work
modelsim.ini
*.rep
transcript
*.ini
*.wlf
*.log

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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