Changes to support vectored RVFI_DII bridge directly, which enables us to successfully run memory tests with traps.
This commit is contained in:
@@ -179,6 +179,9 @@ module mkCore#(CoreId coreId)(Core);
|
||||
`ifdef RVFI_DII
|
||||
Toooba_RVFI_DII_Bridge_IFC rvfi_bridge <- mkTooobaRVFIDIIBridge;
|
||||
mkConnection(rvfi_bridge.dii, fetchStage.dii);
|
||||
rule rl_passLastId;
|
||||
fetchStage.lastTraceId(rvfi_bridge.lastId);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// back end
|
||||
|
||||
@@ -40,7 +40,7 @@ import VerificationPacket::*;
|
||||
import RenameDebugIF::*;
|
||||
|
||||
`ifdef RVFI
|
||||
import RVFI_DII :: *;
|
||||
import RVFI_DII_Types::*;
|
||||
`endif
|
||||
|
||||
typedef struct {
|
||||
@@ -131,41 +131,47 @@ typedef struct {
|
||||
} CommitTrap deriving(Bits, Eq, FShow);
|
||||
|
||||
`ifdef RVFI
|
||||
function RVFI_DII_Execution#(DataSz,DataSz) genRVFI(ToReorderBuffer rot);
|
||||
Addr addr = 0;
|
||||
Addr next_pc = rot.pc + 4;
|
||||
Data data = rot.traceBundle.regWriteData;
|
||||
case (rot.ppc_vaddr_csrData) matches
|
||||
tagged VAddr .vaddr: addr = vaddr;
|
||||
tagged PPC .ppc: next_pc = ppc;
|
||||
tagged CSRData .csrdata: data = csrdata;
|
||||
endcase
|
||||
ByteEn rmask = replicate(False);
|
||||
ByteEn wmask = replicate(False);
|
||||
case (rot.lsqTag) matches
|
||||
tagged Ld .l: rmask = rot.traceBundle.memByteEn;
|
||||
tagged St .s: wmask = rot.traceBundle.memByteEn;
|
||||
endcase
|
||||
return RVFI_DII_Execution {
|
||||
rvfi_order: 0, // Instruction number? InstID maybe?
|
||||
rvfi_trap: isValid(rot.trap),
|
||||
rvfi_halt: False,
|
||||
rvfi_intr: ?,
|
||||
rvfi_insn: rot.orig_inst,
|
||||
rvfi_rs1_addr: rot.orig_inst[19:15],
|
||||
rvfi_rs2_addr: rot.orig_inst[24:20],
|
||||
rvfi_rs1_data: ?,
|
||||
rvfi_rs2_data: ?,
|
||||
rvfi_pc_rdata: rot.pc,
|
||||
rvfi_pc_wdata: next_pc,
|
||||
rvfi_mem_wdata: 0,
|
||||
rvfi_rd_addr: rot.orig_inst[11:7],
|
||||
rvfi_rd_wdata: ((rot.orig_inst[11:7]==0) ? 0:data),
|
||||
rvfi_mem_addr: addr,
|
||||
rvfi_mem_rmask: pack(rmask),
|
||||
rvfi_mem_wmask: pack(wmask),
|
||||
rvfi_mem_rdata: data
|
||||
};
|
||||
function Maybe#(RVFI_DII_Execution#(DataSz,DataSz)) genRVFI(ToReorderBuffer rot, Dii_Id traceCnt);
|
||||
Addr addr = 0;
|
||||
Addr next_pc = 0;
|
||||
Data data = 0;
|
||||
ByteEn rmask = replicate(False);
|
||||
ByteEn wmask = replicate(False);
|
||||
if (!isValid(rot.trap)) begin
|
||||
next_pc = rot.pc + 4;
|
||||
data = rot.traceBundle.regWriteData;
|
||||
case (rot.ppc_vaddr_csrData) matches
|
||||
tagged VAddr .vaddr: begin
|
||||
addr = vaddr;
|
||||
case (rot.lsqTag) matches
|
||||
tagged Ld .l: rmask = rot.traceBundle.memByteEn;
|
||||
tagged St .s: wmask = rot.traceBundle.memByteEn;
|
||||
endcase
|
||||
end
|
||||
tagged PPC .ppc: next_pc = ppc;
|
||||
tagged CSRData .csrdata: data = csrdata;
|
||||
endcase
|
||||
end
|
||||
return tagged Valid RVFI_DII_Execution {
|
||||
rvfi_order: zeroExtend(pack(traceCnt)),
|
||||
rvfi_trap: isValid(rot.trap),
|
||||
rvfi_halt: False,
|
||||
rvfi_intr: ?,
|
||||
rvfi_insn: rot.orig_inst,
|
||||
rvfi_rs1_addr: rot.orig_inst[19:15],
|
||||
rvfi_rs2_addr: rot.orig_inst[24:20],
|
||||
rvfi_rs1_data: ?,
|
||||
rvfi_rs2_data: ?,
|
||||
rvfi_pc_rdata: rot.pc,
|
||||
rvfi_pc_wdata: next_pc,
|
||||
rvfi_mem_wdata: 0,
|
||||
rvfi_rd_addr: rot.orig_inst[11:7],
|
||||
rvfi_rd_wdata: ((rot.orig_inst[11:7]==0) ? 0:data),
|
||||
rvfi_mem_addr: addr,
|
||||
rvfi_mem_rmask: pack(rmask),
|
||||
rvfi_mem_wmask: pack(wmask),
|
||||
rvfi_mem_rdata: data
|
||||
};
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
@@ -230,6 +236,7 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
`ifdef RVFI
|
||||
// RVFI trace report. Not an input?
|
||||
FIFO#(Rvfi_Traces) rvfiQ <- mkFIFO;
|
||||
Reg#(Dii_Id) traceCnt <- mkReg(0);
|
||||
`endif
|
||||
|
||||
// deadlock check
|
||||
@@ -428,9 +435,10 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
$display ("CommitStage.doCommitTrap_flush: commitTrap: ", fshow (commitTrap_val));
|
||||
end
|
||||
`ifdef RVFI
|
||||
Rvfi_Traces rvfis = replicate(RVFI_DII_Execution{rvfi_order: -1});
|
||||
rvfis[0] = genRVFI(x);
|
||||
Rvfi_Traces rvfis = replicate(tagged Invalid);
|
||||
rvfis[0] = genRVFI(x, traceCnt);
|
||||
rvfiQ.enq(rvfis);
|
||||
traceCnt <= traceCnt + 1;
|
||||
`endif
|
||||
|
||||
// flush everything. Only increment epoch and stall fetch when we haven
|
||||
@@ -469,6 +477,7 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
if(trap.trap matches tagged Interrupt .inter) begin
|
||||
inIfc.commitCsrInstOrInterrupt;
|
||||
end
|
||||
if (verbose) $display ("CommitStage.doCommitTrap_handle: ", fshow (commitTrap));
|
||||
|
||||
// trap handling & redirect
|
||||
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
|
||||
@@ -533,9 +542,10 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
rg_instret <= rg_instret + 1;
|
||||
end
|
||||
`ifdef RVFI
|
||||
Rvfi_Traces rvfis = replicate(RVFI_DII_Execution{rvfi_order: -1});
|
||||
rvfis[0] = genRVFI(x);
|
||||
Rvfi_Traces rvfis = replicate(tagged Invalid);
|
||||
rvfis[0] = genRVFI(x, traceCnt);
|
||||
rvfiQ.enq(rvfis);
|
||||
traceCnt <= traceCnt + 1;
|
||||
`endif
|
||||
|
||||
// we claim a phy reg for every inst, so commit its renaming
|
||||
@@ -679,7 +689,8 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
`endif
|
||||
|
||||
`ifdef RVFI
|
||||
Rvfi_Traces rvfis = replicate(RVFI_DII_Execution{rvfi_order: -1});
|
||||
Rvfi_Traces rvfis = replicate(tagged Invalid);
|
||||
SupCnt whichTrace = 0;
|
||||
`endif
|
||||
|
||||
Bit #(64) instret = 0;
|
||||
@@ -698,7 +709,8 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
else begin
|
||||
if (verbose) $display("[doCommitNormalInst - %d] ", i, fshow(inst_tag), " ; ", fshow(x));
|
||||
`ifdef RVFI
|
||||
rvfis[i] = genRVFI(x);
|
||||
rvfis[i] = genRVFI(x, traceCnt + zeroExtend(whichTrace));
|
||||
whichTrace = whichTrace + 1;
|
||||
`endif
|
||||
|
||||
if (verbosity > 0) begin
|
||||
@@ -805,6 +817,7 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
`endif
|
||||
`ifdef RVFI
|
||||
rvfiQ.enq(rvfis);
|
||||
traceCnt <= traceCnt + zeroExtend(whichTrace);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
|
||||
@@ -80,18 +80,18 @@ function Action doAssert(Bool b, String s) = dynamicAssert(b, s);
|
||||
`endif
|
||||
|
||||
`ifdef RVFI_DII
|
||||
typedef 8 SEQ_LEN;
|
||||
typedef UInt#(SEQ_LEN) Dii_Id;
|
||||
typedef Vector#(`sizeSup, RVFI_DII_Execution #(DataSz,DataSz)) Rvfi_Traces;
|
||||
typedef Vector#(`sizeSup, Maybe#(RVFI_DII_Execution #(DataSz,DataSz))) Rvfi_Traces;
|
||||
typedef Vector#(`sizeSup, Maybe#(Dii_Id)) Dii_Ids;
|
||||
typedef Vector#(`sizeSup, Maybe#(Bit#(32))) Dii_Insts;
|
||||
|
||||
typedef struct {
|
||||
Vector#(`sizeSup, Maybe#(Instruction)) insts;
|
||||
Vector#(`sizeSup, Dii_Id) ids;
|
||||
Dii_Insts insts;
|
||||
Dii_Ids ids;
|
||||
} InstsAndIDs deriving(Bits, Eq, FShow);
|
||||
|
||||
interface Toooba_RVFI_DII_Server;
|
||||
interface Get#(Dii_Id) seqReq;
|
||||
interface Put#(Tuple2#(Bit#(32), Dii_Id)) inst;
|
||||
interface Get#(RVFI_DII_Execution#(DataSz, DataSz)) trace_report;
|
||||
interface Get#(Dii_Ids) seqReq;
|
||||
interface Put#(InstsAndIDs) inst;
|
||||
interface Get#(Rvfi_Traces) trace_report;
|
||||
endinterface
|
||||
`endif
|
||||
|
||||
@@ -72,7 +72,7 @@ import TV_Info :: *;
|
||||
`endif
|
||||
|
||||
`ifdef RVFI_DII
|
||||
import RVFI_DII :: *;
|
||||
import RVFI_DII_Types :: *;
|
||||
import Types :: *;
|
||||
`endif
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ package Top_HW_Side;
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
@@ -300,6 +302,7 @@ endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkTop_HW_Side(Empty)
|
||||
|
||||
provisos (Add#(a__, TDiv#(DataSz,8), 8), Add#(b__, DataSz, 64), Add#(c__, TDiv#(DataSz,8), 8), Add#(d__, DataSz, 64));
|
||||
|
||||
Reg #(Bool) rg_banner_printed <- mkReg (False);
|
||||
@@ -314,15 +317,14 @@ module mkTop_HW_Side(Empty)
|
||||
rg_banner_printed <= True;
|
||||
endrule
|
||||
|
||||
RVFI_DII_Bridge #(DataSz, DataSz, SEQ_LEN) bridge <- mkRVFI_DII_Bridge("", 5001);
|
||||
RVFI_DII_Bridge #(DataSz, DataSz, `sizeSup) bridge <- mkRVFI_DII_Bridge("", 5001);
|
||||
let dut <- mkPre_Top_HW_Side(reset_by bridge.new_rst);
|
||||
mkConnection(bridge.client.report, dut.trace_report);
|
||||
|
||||
(* descending_urgency = "bridge.handleReset, rl_provide_instr" *)
|
||||
rule rl_provide_instr;
|
||||
Dii_Id req <- dut.seqReq.get;
|
||||
Bit#(32) inst <- bridge.client.getInst(req);
|
||||
dut.inst.put(tuple2(inst, req));
|
||||
Dii_Ids reqs <- dut.seqReq.get;
|
||||
Dii_Insts insts <- bridge.client.getInst(reqs);
|
||||
dut.inst.put(InstsAndIDs{insts: insts, ids: reqs});
|
||||
endrule
|
||||
endmodule
|
||||
|
||||
|
||||
Submodule src_Verifier/BSV-RVFI-DII updated: 380f77da5b...b64f7a532d
@@ -43,8 +43,10 @@ import ConfigReg :: *;
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import ProcTypes::*;
|
||||
|
||||
//import Verifier :: *;
|
||||
import RVFI_DII :: *;
|
||||
@@ -53,74 +55,51 @@ import RVFI_DII :: *;
|
||||
|
||||
interface Toooba_RVFI_DII_Bridge_IFC;
|
||||
interface Toooba_RVFI_DII_Server rvfi_dii_server;
|
||||
interface Server#(Dii_Id, InstsAndIDs) dii;
|
||||
interface Server#(Dii_Ids, InstsAndIDs) dii;
|
||||
interface Put#(Rvfi_Traces) rvfi;
|
||||
method Dii_Id lastId;
|
||||
endinterface
|
||||
|
||||
module mkTooobaRVFIDIIBridge(Toooba_RVFI_DII_Bridge_IFC);
|
||||
// DII state
|
||||
FIFOF#(Tuple2#(Bit#(32), Dii_Id)) dii_in <- mkUGFIFOF;
|
||||
Reg#(InstsAndIDs) buff <- mkConfigRegU;
|
||||
FIFOF#(InstsAndIDs) instrs <- mkSizedFIFOF(2048);
|
||||
PulseWire putFromBridge <- mkPulseWire;
|
||||
// RVFI state
|
||||
FIFO#(Rvfi_Traces) report_vectors <- mkSizedFIFO(2048);
|
||||
FIFO#(RVFI_DII_Execution#(DataSz,DataSz)) reports <- mkFIFO;
|
||||
// Request ID
|
||||
FIFO#(Dii_Id) seq_req <- mkFIFO;
|
||||
|
||||
Bit#(32) nop = 'h01FFF033;
|
||||
FIFO#(Dii_Ids) seq_req <- mkFIFO;
|
||||
Reg#(Dii_Id) last_id <- mkReg(0);
|
||||
|
||||
Bool verbose = True;
|
||||
|
||||
function Bool validReport(RVFI_DII_Execution#(DataSz,DataSz) trace);
|
||||
return (trace.rvfi_order != -1 && trace.rvfi_insn != nop);
|
||||
return (trace.rvfi_insn != dii_nop);
|
||||
endfunction
|
||||
|
||||
Reg#(SupWaySel) report_select <- mkReg(0);
|
||||
rule split_report_vectors;
|
||||
RVFI_DII_Execution#(DataSz,DataSz) report = report_vectors.first[report_select];
|
||||
if (verbose)
|
||||
$display("%t RVFI response: ", $time,
|
||||
fshow(report_vectors.first[report_select])
|
||||
);
|
||||
if (validReport(report)) reports.enq(report);
|
||||
if (report_select == -1) report_vectors.deq();
|
||||
report_select <= report_select + 1;
|
||||
endrule
|
||||
// These two functions convert beteween "Invalid" instructions and "nops".
|
||||
// This is because the pipeline currently isn't able to handle Invalid injections,
|
||||
// so we replace them with special nops in the bridge that we can filter out in the rvfi trace stream.
|
||||
function Maybe#(Bit#(32)) maybeToNop(Maybe#(Bit#(32)) in);
|
||||
return tagged Valid fromMaybe(dii_nop, in);
|
||||
endfunction
|
||||
|
||||
Reg#(SupWaySel) buffLvl <- mkConfigReg(0);
|
||||
rule bufferInsts(buffLvl != 0 || dii_in.notEmpty);
|
||||
InstsAndIDs cb = buff;
|
||||
Bit#(32) ins = nop;
|
||||
Dii_Id id = ?;
|
||||
if (dii_in.notEmpty) {ins, id} <- toGet(dii_in).get;
|
||||
cb.insts[buffLvl] = tagged Valid ins;
|
||||
cb.ids[buffLvl] = id;
|
||||
if (buffLvl == -1) begin
|
||||
instrs.enq(cb);
|
||||
cb.insts = replicate(tagged Invalid);
|
||||
end
|
||||
buff <= cb;
|
||||
buffLvl <= buffLvl + 1;
|
||||
endrule
|
||||
function Maybe#(RVFI_DII_Execution #(DataSz,DataSz)) nopToMaybe(Maybe#(RVFI_DII_Execution #(DataSz,DataSz)) in);
|
||||
Maybe#(RVFI_DII_Execution #(DataSz,DataSz)) ret = in;
|
||||
if (ret matches tagged Valid .val &&& !validReport(val)) ret = tagged Invalid;
|
||||
return (ret);
|
||||
endfunction
|
||||
|
||||
interface Toooba_RVFI_DII_Server rvfi_dii_server;
|
||||
interface Get seqReq = toGet(seq_req);
|
||||
interface Put inst;
|
||||
method Action put(Tuple2#(Bit#(32), Dii_Id) in) if (dii_in.notFull);
|
||||
dii_in.enq(in);
|
||||
putFromBridge.send();
|
||||
endmethod
|
||||
endinterface
|
||||
interface Get trace_report = toGet(reports);
|
||||
interface Put inst = toPut(instrs);
|
||||
interface Get trace_report = toGet(report_vectors);
|
||||
endinterface
|
||||
|
||||
interface Server dii;
|
||||
interface Put request = toPut(seq_req);
|
||||
interface Get response;
|
||||
method ActionValue#(InstsAndIDs) get if (!putFromBridge);
|
||||
method ActionValue#(InstsAndIDs) get;
|
||||
InstsAndIDs insts = instrs.first();
|
||||
insts.insts = map(maybeToNop, insts.insts);
|
||||
instrs.deq();
|
||||
if (verbose)
|
||||
$display("%t DII injection: ", $time,
|
||||
@@ -131,7 +110,21 @@ module mkTooobaRVFIDIIBridge(Toooba_RVFI_DII_Bridge_IFC);
|
||||
endinterface
|
||||
endinterface
|
||||
|
||||
interface Put rvfi = toPut(report_vectors);
|
||||
interface Put rvfi;
|
||||
method Action put(Rvfi_Traces in);
|
||||
Rvfi_Traces out = map(nopToMaybe,in);
|
||||
report_vectors.enq(out);
|
||||
Dii_Id next_id = last_id;
|
||||
for (Integer i = 0; i < `sizeSup; i = i + 1) begin
|
||||
if (out[i] matches tagged Valid .rpt) begin
|
||||
Dii_Id this_id = unpack(truncate(rpt.rvfi_order));
|
||||
if (this_id > next_id) next_id = this_id;
|
||||
end
|
||||
end
|
||||
last_id <= next_id;
|
||||
endmethod
|
||||
endinterface
|
||||
method Dii_Id lastId = last_id;
|
||||
endmodule
|
||||
|
||||
endpackage
|
||||
|
||||
@@ -1,519 +0,0 @@
|
||||
|
||||
// Copyright (c) 2000-2012 Bluespec, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// $Revision$
|
||||
// $Date$
|
||||
|
||||
`ifdef BSV_ASSIGNMENT_DELAY
|
||||
`else
|
||||
`define BSV_ASSIGNMENT_DELAY
|
||||
`endif
|
||||
|
||||
`ifdef BSV_POSITIVE_RESET
|
||||
`define BSV_RESET_VALUE 1'b1
|
||||
`define BSV_RESET_EDGE posedge
|
||||
`else
|
||||
`define BSV_RESET_VALUE 1'b0
|
||||
`define BSV_RESET_EDGE negedge
|
||||
`endif
|
||||
|
||||
`ifdef BSV_RESET_FIFO_HEAD
|
||||
`define BSV_RESET_EDGE_HEAD or `BSV_RESET_EDGE dRST
|
||||
`else
|
||||
`define BSV_RESET_EDGE_HEAD
|
||||
`endif
|
||||
|
||||
|
||||
// A clock synchronization FIFO where the enqueue and dequeue sides are in
|
||||
// different clock domains.
|
||||
// There are no restrictions w.r.t. clock frequencies
|
||||
// The depth of the FIFO must be a power of 2 (2,4,8,...) since the
|
||||
// indexing uses a Gray code counter.
|
||||
// FULL and EMPTY signal are pessimistic, that is, they are asserted
|
||||
// immediately when the FIFO becomes FULL or EMPTY, but their deassertion
|
||||
// is delayed due to synchronization latency.
|
||||
// dCount and sCount are also delayed and may differ because of latency
|
||||
// from the synchronization logic
|
||||
module SyncFIFOLevel(
|
||||
sCLK,
|
||||
sRST,
|
||||
dCLK,
|
||||
sENQ,
|
||||
sD_IN,
|
||||
sFULL_N,
|
||||
dDEQ,
|
||||
dD_OUT,
|
||||
dEMPTY_N,
|
||||
dCOUNT,
|
||||
sCOUNT,
|
||||
sCLR,
|
||||
sCLR_RDY,
|
||||
dCLR,
|
||||
dCLR_RDY
|
||||
) ;
|
||||
|
||||
|
||||
parameter dataWidth = 1 ;
|
||||
parameter depth = 2 ; // minimum 2
|
||||
parameter indxWidth = 1 ; // minimum 1
|
||||
|
||||
// input clock domain ports
|
||||
input sCLK ;
|
||||
input sRST ;
|
||||
input sENQ ;
|
||||
input [dataWidth -1 : 0] sD_IN ;
|
||||
output sFULL_N ;
|
||||
|
||||
// destination clock domain ports
|
||||
input dCLK ;
|
||||
input dDEQ ;
|
||||
output dEMPTY_N ;
|
||||
output [dataWidth -1 : 0] dD_OUT ;
|
||||
|
||||
// Counts of capacity need extra bit to show full, e.g., range is 0 to 32
|
||||
output [indxWidth : 0] dCOUNT;
|
||||
output [indxWidth : 0] sCOUNT;
|
||||
|
||||
// Clear signals on both domains
|
||||
input sCLR;
|
||||
output sCLR_RDY;
|
||||
input dCLR;
|
||||
output dCLR_RDY;
|
||||
|
||||
// constants for bit masking of the gray code
|
||||
wire [indxWidth : 0] msbset = ~({(indxWidth + 1){1'b1}} >> 1) ;
|
||||
wire [indxWidth - 1 : 0] msb2set = ~({(indxWidth + 0){1'b1}} >> 1) ;
|
||||
wire [indxWidth : 0] msb12set = msbset | {1'b0, msb2set} ; // 'b11000...
|
||||
|
||||
// FIFO Memory
|
||||
reg [dataWidth -1 : 0] fifoMem [0: depth -1 ] ;
|
||||
reg [dataWidth -1 : 0] dDoutReg ;
|
||||
|
||||
// Enqueue Pointer
|
||||
reg [indxWidth : 0] sGEnqPtr, sBEnqPtr ; // Flops
|
||||
reg sNotFullReg ;
|
||||
wire [indxWidth : 0] sNextGEnqPtr, sNextBEnqPtr ;
|
||||
wire [indxWidth : 0] sNextCnt, sFutureCnt ;
|
||||
wire sNextNotFull, sFutureNotFull ;
|
||||
|
||||
// Dequeue Pointer
|
||||
reg [indxWidth : 0] dGDeqPtr, dBDeqPtr ; // Flops
|
||||
reg dNotEmptyReg ;
|
||||
wire [indxWidth : 0] dNextGDeqPtr, dNextBDeqPtr ;
|
||||
wire [indxWidth : 0] dNextCnt ;
|
||||
wire dNextNotEmpty;
|
||||
|
||||
|
||||
// Rgisters needed for capacity counts
|
||||
reg [indxWidth : 0] sCountReg, dCountReg ;
|
||||
|
||||
// Note for Timing improvement:
|
||||
// These signals can be registers to improve a long path from the
|
||||
// second stage of the synchronizer to the input of the
|
||||
// CountReg. The path includes a Gray to Binary conversion and a
|
||||
// subtraction, which can easily be a long path.
|
||||
// The effect is that the count is delayed one additional cycle.
|
||||
wire [indxWidth : 0] sBDeqPtr, dBEnqPtr ;
|
||||
|
||||
// flops to sychronize enqueue and dequeue point across domains
|
||||
reg [indxWidth : 0] dSyncReg1, dEnqPtr ;
|
||||
reg [indxWidth : 0] sSyncReg1, sDeqPtr ;
|
||||
|
||||
// Indexes for fifo memory is one bit smaller than indexes
|
||||
wire [indxWidth - 1 :0] sEnqPtrIndx, dDeqPtrIndx ;
|
||||
|
||||
// wires needed for clear processing
|
||||
wire dRST;
|
||||
wire sCLRSynced; // dCLR synced to sCLK
|
||||
wire sCLR_RDY_int;
|
||||
|
||||
wire dCLRSynced; // sCLR synced to dCLK
|
||||
wire dCLR_RDY_int;
|
||||
|
||||
wire sClear;
|
||||
wire dClear;
|
||||
|
||||
// Clear processing requires the use of 2 handshake synchronizers
|
||||
SyncHandshake #(.delayreturn(1))
|
||||
sClrSync ( .sCLK(sCLK),
|
||||
.sRST(sRST),
|
||||
.dCLK(dCLK),
|
||||
.sEN(sCLR),
|
||||
.sRDY(sCLR_RDY_int),
|
||||
.dPulse(dCLRSynced));
|
||||
|
||||
SyncHandshake #(.delayreturn(1))
|
||||
dClrSync ( .sCLK(dCLK),
|
||||
.sRST(sRST),
|
||||
.dCLK(sCLK),
|
||||
.sEN(dCLR),
|
||||
.sRDY(dCLR_RDY_int),
|
||||
.dPulse(sCLRSynced));
|
||||
|
||||
// Outputs
|
||||
assign dD_OUT = dDoutReg;
|
||||
assign dEMPTY_N = dNotEmptyReg ;
|
||||
assign sFULL_N = sNotFullReg ;
|
||||
assign sCOUNT = sCountReg;
|
||||
assign dCOUNT = dCountReg;
|
||||
assign sCLR_RDY = sCLR_RDY_int;
|
||||
assign dCLR_RDY = dCLR_RDY_int;
|
||||
|
||||
// Indexes are truncated from the Binary counter
|
||||
assign sEnqPtrIndx = sBEnqPtr[indxWidth-1:0] ;
|
||||
assign dDeqPtrIndx = dBDeqPtr[indxWidth-1:0] ;
|
||||
|
||||
// clear signals
|
||||
assign sClear = sCLR || !sCLR_RDY_int || sCLRSynced;
|
||||
assign dClear = dCLR || !dCLR_RDY_int || dCLRSynced;
|
||||
assign dRST = sRST;
|
||||
|
||||
// Fifo memory write
|
||||
always @(posedge sCLK)
|
||||
begin
|
||||
if ( sENQ )
|
||||
fifoMem[sEnqPtrIndx] <= `BSV_ASSIGNMENT_DELAY sD_IN ;
|
||||
end // always @ (posedge sCLK)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Enqueue Pointer and increment logic
|
||||
assign sNextBEnqPtr = sBEnqPtr + 1'b1 ;
|
||||
assign sNextGEnqPtr = sNextBEnqPtr ^ (sNextBEnqPtr >> 1) ;
|
||||
assign sNextNotFull = (sGEnqPtr ^ msb12set) != sDeqPtr ;
|
||||
assign sFutureNotFull = (sNextGEnqPtr ^ msb12set) != sDeqPtr ;
|
||||
assign sNextCnt = sBEnqPtr - sBDeqPtr ;
|
||||
assign sFutureCnt = sNextBEnqPtr - sBDeqPtr ;
|
||||
assign sBDeqPtr = grayToBinary( sDeqPtr ) ;
|
||||
|
||||
|
||||
always @(posedge sCLK or `BSV_RESET_EDGE sRST)
|
||||
begin
|
||||
if (sRST == `BSV_RESET_VALUE)
|
||||
begin
|
||||
sBEnqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth +1 ) {1'b0}} ;
|
||||
sGEnqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth +1 ) {1'b0}} ;
|
||||
sNotFullReg <= `BSV_ASSIGNMENT_DELAY 1'b0 ; // Mark as full during reset
|
||||
sCountReg <= `BSV_ASSIGNMENT_DELAY {(indxWidth +1 ) {1'b0}} ;
|
||||
end // if (sRST == `BSV_RESET_VALUE)
|
||||
else
|
||||
begin
|
||||
if (sClear)
|
||||
begin
|
||||
sBEnqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth +1 ) {1'b0}} ;
|
||||
sGEnqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth +1 ) {1'b0}} ;
|
||||
sNotFullReg <= `BSV_ASSIGNMENT_DELAY 1'b0 ;
|
||||
sCountReg <= `BSV_ASSIGNMENT_DELAY {(indxWidth +1 ) {1'b0}} ;
|
||||
end
|
||||
else if ( sENQ )
|
||||
begin
|
||||
sBEnqPtr <= `BSV_ASSIGNMENT_DELAY sNextBEnqPtr ;
|
||||
sGEnqPtr <= `BSV_ASSIGNMENT_DELAY sNextGEnqPtr ;
|
||||
sNotFullReg <= `BSV_ASSIGNMENT_DELAY sFutureNotFull ;
|
||||
sCountReg <= `BSV_ASSIGNMENT_DELAY sFutureCnt ;
|
||||
end
|
||||
else
|
||||
begin
|
||||
sNotFullReg <= `BSV_ASSIGNMENT_DELAY sNextNotFull ;
|
||||
sCountReg <= `BSV_ASSIGNMENT_DELAY sNextCnt ;
|
||||
end // else: !if( sENQ )
|
||||
end // else: !if(sRST == `BSV_RESET_VALUE)
|
||||
end // always @ (posedge sCLK or `BSV_RESET_EDGE sRST)
|
||||
|
||||
// Enqueue pointer synchronizer to dCLK
|
||||
always @(posedge dCLK or `BSV_RESET_EDGE sRST)
|
||||
begin
|
||||
if (sRST == `BSV_RESET_VALUE)
|
||||
begin
|
||||
dSyncReg1 <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
dEnqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
end // if (sRST == `BSV_RESET_VALUE)
|
||||
else
|
||||
begin
|
||||
dSyncReg1 <= `BSV_ASSIGNMENT_DELAY sGEnqPtr ; // Clock domain crossing
|
||||
dEnqPtr <= `BSV_ASSIGNMENT_DELAY dSyncReg1 ;
|
||||
end // else: !if(sRST == `BSV_RESET_VALUE)
|
||||
end // always @ (posedge dCLK or `BSV_RESET_EDGE sRST)
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Enqueue Pointer and increment logic
|
||||
assign dNextBDeqPtr = dBDeqPtr + 1'b1 ;
|
||||
assign dNextGDeqPtr = dNextBDeqPtr ^ (dNextBDeqPtr >> 1) ;
|
||||
assign dNextNotEmpty = dGDeqPtr != dEnqPtr ;
|
||||
assign dNextCnt = dBEnqPtr - dBDeqPtr ;
|
||||
assign dBEnqPtr = grayToBinary( dEnqPtr ) ;
|
||||
|
||||
always @(posedge dCLK or `BSV_RESET_EDGE dRST)
|
||||
begin
|
||||
if (dRST == `BSV_RESET_VALUE)
|
||||
begin
|
||||
dBDeqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
dGDeqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
dNotEmptyReg <= `BSV_ASSIGNMENT_DELAY 1'b0 ; // Mark as empty to avoid dequeues until after reset
|
||||
dCountReg <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
end // if (sRST == `BSV_RESET_VALUE)
|
||||
else
|
||||
begin
|
||||
if (dClear) begin
|
||||
dBDeqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
dGDeqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
dNotEmptyReg <= `BSV_ASSIGNMENT_DELAY 1'b0 ;
|
||||
dCountReg <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
end
|
||||
else if (!dNotEmptyReg && dNextNotEmpty) begin
|
||||
dBDeqPtr <= `BSV_ASSIGNMENT_DELAY dNextBDeqPtr ;
|
||||
dGDeqPtr <= `BSV_ASSIGNMENT_DELAY dNextGDeqPtr ;
|
||||
dNotEmptyReg <= `BSV_ASSIGNMENT_DELAY 1'b1 ;
|
||||
dCountReg <= `BSV_ASSIGNMENT_DELAY dNextCnt ;
|
||||
end
|
||||
else if (dDEQ && dNextNotEmpty) begin
|
||||
dBDeqPtr <= `BSV_ASSIGNMENT_DELAY dNextBDeqPtr ;
|
||||
dGDeqPtr <= `BSV_ASSIGNMENT_DELAY dNextGDeqPtr ;
|
||||
dNotEmptyReg <= `BSV_ASSIGNMENT_DELAY 1'b1 ;
|
||||
dCountReg <= `BSV_ASSIGNMENT_DELAY dNextCnt ;
|
||||
end
|
||||
else if (dDEQ && !dNextNotEmpty) begin
|
||||
dNotEmptyReg <= `BSV_ASSIGNMENT_DELAY 1'b0 ;
|
||||
dCountReg <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
end
|
||||
else begin
|
||||
dCountReg <= `BSV_ASSIGNMENT_DELAY dNextCnt ;
|
||||
end
|
||||
end // else: !if(sRST == `BSV_RESET_VALUE)
|
||||
end // always @ (posedge dCLK or `BSV_RESET_EDGE sRST)
|
||||
|
||||
always @(posedge dCLK `BSV_RESET_EDGE_HEAD)
|
||||
begin
|
||||
`ifdef BSV_RESET_FIFO_HEAD
|
||||
if (dRST == `BSV_RESET_VALUE)
|
||||
begin
|
||||
dDoutReg <= `BSV_ASSIGNMENT_DELAY { dataWidth { 1'b0 }} ;
|
||||
end // if (dRST == `BSV_RESET_VALUE)
|
||||
else
|
||||
`endif
|
||||
begin
|
||||
if ((!dNotEmptyReg || dDEQ) && dNextNotEmpty) begin
|
||||
dDoutReg <= `BSV_ASSIGNMENT_DELAY fifoMem[dDeqPtrIndx] ;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Dequeue pointer synchronized to sCLK
|
||||
always @(posedge sCLK or `BSV_RESET_EDGE sRST)
|
||||
begin
|
||||
if (sRST == `BSV_RESET_VALUE)
|
||||
begin
|
||||
sSyncReg1 <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
sDeqPtr <= `BSV_ASSIGNMENT_DELAY {(indxWidth + 1) {1'b0}} ;
|
||||
end // if (sRST == `BSV_RESET_VALUE)
|
||||
else
|
||||
begin
|
||||
sSyncReg1 <= `BSV_ASSIGNMENT_DELAY dGDeqPtr ; // clock domain crossing
|
||||
sDeqPtr <= `BSV_ASSIGNMENT_DELAY sSyncReg1 ;
|
||||
// sBDeqPtr <= `BSV_ASSIGNMENT_DELAY grayToBinary( sDeqPtr ) ;
|
||||
end // else: !if(sRST == `BSV_RESET_VALUE)
|
||||
end // always @ (posedge sCLK or `BSV_RESET_EDGE sRST)
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// synopsys translate_off
|
||||
// Run time assertion check
|
||||
always @(posedge sCLK)
|
||||
begin
|
||||
if ( sENQ && ! sNotFullReg ) $display ("Warning: SyncFIFOLevel: %m -- Enqueing to a full fifo");
|
||||
end
|
||||
always @(posedge dCLK)
|
||||
begin
|
||||
if ( dDEQ && ! dNotEmptyReg ) $display ("Warning: SyncFIFOLevel: %m -- Dequeuing from empty fifo");
|
||||
end
|
||||
// synopsys translate_on
|
||||
|
||||
`ifdef BSV_NO_INITIAL_BLOCKS
|
||||
`else // not BSV_NO_INITIAL_BLOCKS
|
||||
// synopsys translate_off
|
||||
initial
|
||||
begin : initBlock
|
||||
integer i ;
|
||||
|
||||
// initialize the FIFO memory with aa's
|
||||
for (i = 0; i < depth; i = i + 1)
|
||||
begin
|
||||
fifoMem[i] = {((dataWidth + 1)/2){2'b10}} ;
|
||||
end
|
||||
dDoutReg = {((dataWidth + 1)/2){2'b10}} ;
|
||||
|
||||
// initialize the pointer
|
||||
sGEnqPtr = {((indxWidth + 1)/2){2'b10}} ;
|
||||
sBEnqPtr = sGEnqPtr ;
|
||||
sNotFullReg = 1'b0 ;
|
||||
|
||||
dGDeqPtr = sGEnqPtr ;
|
||||
dBDeqPtr = sGEnqPtr ;
|
||||
dNotEmptyReg = 1'b0;
|
||||
|
||||
|
||||
// initialize other registers
|
||||
sSyncReg1 = sGEnqPtr ;
|
||||
sDeqPtr = sGEnqPtr ;
|
||||
dSyncReg1 = sGEnqPtr ;
|
||||
dEnqPtr = sGEnqPtr ;
|
||||
end // initial begin
|
||||
// synopsys translate_on
|
||||
|
||||
// synopsys translate_off
|
||||
initial
|
||||
begin : parameter_assertions
|
||||
integer ok ;
|
||||
integer i, expDepth ;
|
||||
|
||||
ok = 1;
|
||||
expDepth = 1 ;
|
||||
|
||||
// calculate x = 2 ** (indxWidth - 1)
|
||||
for( i = 0 ; i < indxWidth ; i = i + 1 )
|
||||
begin
|
||||
expDepth = expDepth * 2 ;
|
||||
end
|
||||
if ( expDepth != depth )
|
||||
begin
|
||||
ok = 0;
|
||||
$display ( "ERROR SyncFiFOLevel.v: index size and depth do not match;" ) ;
|
||||
$display ( "\tdepth must equal 2 ** index size. expected %0d", expDepth );
|
||||
end
|
||||
|
||||
#0
|
||||
if ( ok == 0 ) $finish ;
|
||||
|
||||
end // initial begin
|
||||
// synopsys translate_on
|
||||
`endif // BSV_NO_INITIAL_BLOCKS
|
||||
|
||||
function [indxWidth:0] grayToBinary ;
|
||||
input [indxWidth:0] grayin;
|
||||
begin: grayToBinary_block
|
||||
reg [indxWidth:0] binary ;
|
||||
integer i ;
|
||||
for ( i = 0 ; i <= indxWidth ; i = i+1 )
|
||||
begin
|
||||
binary[i] = ^( grayin >> i ) ;
|
||||
end
|
||||
grayToBinary = binary ;
|
||||
end
|
||||
endfunction
|
||||
|
||||
endmodule // FIFOSync
|
||||
|
||||
|
||||
|
||||
|
||||
`ifdef testBluespec
|
||||
module testSyncFIFOLevel() ;
|
||||
parameter dsize = 8;
|
||||
parameter fifodepth = 32;
|
||||
parameter fifoidx = 5;
|
||||
|
||||
wire sCLK, dCLK, dRST ;
|
||||
wire sENQ, dDEQ;
|
||||
wire sFULL_N, dEMPTY_N ;
|
||||
wire [dsize -1:0] sDIN, dDOUT ;
|
||||
|
||||
reg [dsize -1:0] sCNT, dCNT ;
|
||||
reg sRST ;
|
||||
|
||||
wire [fifoidx:0] dItemCnt, sItemCnt ;
|
||||
wire sCLR_RDY;
|
||||
wire dCLR_RDY;
|
||||
wire sCLR;
|
||||
wire dCLR;
|
||||
reg [31:0] count ;
|
||||
reg started ;
|
||||
reg ddeq ;
|
||||
|
||||
|
||||
ClockGen#(14,15,10) sc( sCLK );
|
||||
ClockGen#(11,12,2600) dc( dCLK ); // Pause the generation of the destination side clock
|
||||
|
||||
initial
|
||||
begin
|
||||
sCNT = 0;
|
||||
dCNT = 0;
|
||||
sRST = `BSV_RESET_VALUE ;
|
||||
count = 0;
|
||||
started = 0;
|
||||
ddeq = 0;
|
||||
|
||||
$display( "running test" ) ;
|
||||
|
||||
$dumpfile("SyncFIFOLevel.vcd");
|
||||
$dumpvars(10,testSyncFIFOLevel) ;
|
||||
#1
|
||||
$dumpon ;
|
||||
#200 ;
|
||||
sRST = !`BSV_RESET_VALUE ;
|
||||
|
||||
|
||||
#50000 $finish ;
|
||||
end
|
||||
|
||||
SyncFIFOLevel #(dsize,fifodepth,fifoidx)
|
||||
dut( sCLK, sRST, dCLK, sENQ, sDIN,
|
||||
sFULL_N, dDEQ, dDOUT, dEMPTY_N, dItemCnt, sItemCnt,
|
||||
sCLR, sCLR_RDY, dCLR, dCLR_RDY );
|
||||
|
||||
assign sDIN = sCNT ;
|
||||
assign sENQ = sFULL_N ;
|
||||
|
||||
assign dCLR = ((count[7:0] == 8'b0010_0011) && dCLR_RDY);
|
||||
assign sCLR = ((count[7:0] == 8'b0000_0001) && sCLR_RDY);
|
||||
|
||||
always @(posedge sCLK)
|
||||
begin
|
||||
count <= count + 1 ;
|
||||
$display( "scount is %d", sItemCnt ) ;
|
||||
if (sENQ )
|
||||
begin
|
||||
sCNT <= `BSV_ASSIGNMENT_DELAY sCNT + 1;
|
||||
$display( "enqueuing is %d", sCNT ) ;
|
||||
end // if (sENQ )
|
||||
end // always @ (posedge sCLK)
|
||||
|
||||
assign dDEQ = ddeq ;
|
||||
|
||||
always @(dItemCnt or dEMPTY_N or started or count)
|
||||
begin
|
||||
ddeq = (count > 40) && dEMPTY_N && (started || dItemCnt > 4);
|
||||
end // always @ (dItemCnt or dEMPTY_N or started)
|
||||
|
||||
always @(posedge dCLK)
|
||||
begin
|
||||
$display( "dcount is %d", dItemCnt ) ;
|
||||
if (ddeq)
|
||||
begin
|
||||
started <= 1;
|
||||
$display( "dequeing %d", dDOUT ) ;
|
||||
end // if (dDEQ )
|
||||
else
|
||||
begin
|
||||
started <= 0;
|
||||
end
|
||||
end // always @ (posedge dCLK)
|
||||
|
||||
endmodule // testSyncFIFO
|
||||
`endif
|
||||
175
src_bsc_lib_RTL/SyncRegister.v
Normal file
175
src_bsc_lib_RTL/SyncRegister.v
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
// Copyright (c) 2000-2013 Bluespec, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// $Revision$
|
||||
// $Date$
|
||||
|
||||
`ifdef BSV_ASSIGNMENT_DELAY
|
||||
`else
|
||||
`define BSV_ASSIGNMENT_DELAY
|
||||
`endif
|
||||
|
||||
`ifdef BSV_POSITIVE_RESET
|
||||
`define BSV_RESET_VALUE 1'b1
|
||||
`define BSV_RESET_EDGE posedge
|
||||
`else
|
||||
`define BSV_RESET_VALUE 1'b0
|
||||
`define BSV_RESET_EDGE negedge
|
||||
`endif
|
||||
|
||||
|
||||
// A register synchronization module across clock domains.
|
||||
// Uses a Handshake Pulse protocol to trigger the load on
|
||||
// destination side registers
|
||||
// Transfer takes 3 dCLK for destination side to see data,
|
||||
// sRDY recovers takes 3 dCLK + 3 sCLK
|
||||
module SyncRegister(
|
||||
sCLK,
|
||||
sRST,
|
||||
dCLK,
|
||||
sEN,
|
||||
sRDY,
|
||||
sD_IN,
|
||||
dD_OUT
|
||||
);
|
||||
parameter width = 1 ;
|
||||
parameter init = { width {1'b0 }} ;
|
||||
|
||||
// Source clock domain ports
|
||||
input sCLK ;
|
||||
input sRST ;
|
||||
input sEN ;
|
||||
input [width -1 : 0] sD_IN ;
|
||||
output sRDY ;
|
||||
|
||||
// Destination clock domain ports
|
||||
input dCLK ;
|
||||
output [width -1 : 0] dD_OUT ;
|
||||
|
||||
wire dPulse ;
|
||||
reg [width -1 : 0] sDataSyncIn ;
|
||||
reg [width -1 : 0] dD_OUT ;
|
||||
|
||||
// instantiate a Handshake Sync
|
||||
SyncHandshake #(.init(0),.delayreturn(1))
|
||||
sync( .sCLK(sCLK), .sRST(sRST),
|
||||
.dCLK(dCLK),
|
||||
.sEN(sEN), .sRDY(sRDY),
|
||||
.dPulse(dPulse) ) ;
|
||||
|
||||
always @(posedge sCLK or `BSV_RESET_EDGE sRST)
|
||||
begin
|
||||
if (sRST == `BSV_RESET_VALUE)
|
||||
begin
|
||||
sDataSyncIn <= `BSV_ASSIGNMENT_DELAY init ;
|
||||
end // if (sRST == `BSV_RESET_VALUE)
|
||||
else
|
||||
begin
|
||||
if ( sEN )
|
||||
begin
|
||||
sDataSyncIn <= `BSV_ASSIGNMENT_DELAY sD_IN ;
|
||||
end // if ( sEN )
|
||||
end // else: !if(sRST == `BSV_RESET_VALUE)
|
||||
end // always @ (posedge sCLK or `BSV_RESET_EDGE sRST)
|
||||
|
||||
|
||||
// Transfer the data to destination domain when dPulsed is asserted.
|
||||
// Setup and hold time are assured since at least 2 dClks occured since
|
||||
// sDataSyncIn have been written.
|
||||
always @(posedge dCLK or `BSV_RESET_EDGE sRST)
|
||||
begin
|
||||
if (sRST == `BSV_RESET_VALUE)
|
||||
begin
|
||||
dD_OUT <= `BSV_ASSIGNMENT_DELAY init ;
|
||||
end // if (sRST == `BSV_RESET_VALUE)
|
||||
else
|
||||
begin
|
||||
if ( dPulse )
|
||||
begin
|
||||
dD_OUT <= `BSV_ASSIGNMENT_DELAY sDataSyncIn ;// clock domain crossing
|
||||
end // if ( dPulse )
|
||||
end // else: !if(sRST == `BSV_RESET_VALUE)
|
||||
end // always @ (posedge dCLK or `BSV_RESET_EDGE sRST)
|
||||
|
||||
|
||||
`ifdef BSV_NO_INITIAL_BLOCKS
|
||||
`else // not BSV_NO_INITIAL_BLOCKS
|
||||
// synopsys translate_off
|
||||
initial
|
||||
begin
|
||||
sDataSyncIn = {((width + 1)/2){2'b10}} ;
|
||||
dD_OUT = {((width + 1)/2){2'b10}} ;
|
||||
end // initial begin
|
||||
// synopsys translate_on
|
||||
`endif // BSV_NO_INITIAL_BLOCKS
|
||||
|
||||
|
||||
endmodule // RegisterSync
|
||||
|
||||
|
||||
|
||||
`ifdef testBluespec
|
||||
module testSyncRegister() ;
|
||||
parameter dsize = 8;
|
||||
|
||||
wire sCLK, sRST, dCLK ;
|
||||
wire sEN ;
|
||||
wire sRDY ;
|
||||
|
||||
reg [dsize -1:0] sCNT ;
|
||||
wire [dsize -1:0] sDIN, dDOUT ;
|
||||
|
||||
ClockGen#(20,9,10) sc( sCLK );
|
||||
ClockGen#(11,12,26) dc( dCLK );
|
||||
|
||||
initial
|
||||
begin
|
||||
sCNT = 0;
|
||||
|
||||
$dumpfile("SyncRegister.dump");
|
||||
$dumpvars(5) ;
|
||||
$dumpon ;
|
||||
#100000 $finish ;
|
||||
end
|
||||
|
||||
SyncRegister #(dsize)
|
||||
dut( sCLK, sRST, dCLK,
|
||||
sEN, sRDY, sDIN,
|
||||
dDOUT ) ;
|
||||
|
||||
|
||||
assign sDIN = sCNT ;
|
||||
assign sEN = sRDY ;
|
||||
|
||||
always @(posedge sCLK)
|
||||
begin
|
||||
if (sRDY )
|
||||
begin
|
||||
sCNT <= `BSV_ASSIGNMENT_DELAY sCNT + 1;
|
||||
end
|
||||
end // always @ (posedge sCLK)
|
||||
|
||||
|
||||
|
||||
endmodule // testSyncFIFO
|
||||
`endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user