diff --git a/src/main/resources/vsrc/AsyncFifoCustomCore.sv b/src/main/resources/vsrc/AsyncFifoCustomCore.sv new file mode 100644 index 00000000..cfca7e7d --- /dev/null +++ b/src/main/resources/vsrc/AsyncFifoCustomCore.sv @@ -0,0 +1,200 @@ +// Code your design here +module AsyncFifoCustomCore #( + parameter DEPTH = 16, + parameter WIDTH = 8 +)( + input rst, + + input clk_w, + input valid_w, + output ready_w, + input [WIDTH-1:0] data_w, + + input clk_r, + output valid_r, + input ready_r, + output [WIDTH-1:0] data_r +); + localparam PTR_WIDTH = $clog2(DEPTH); + + + wire [PTR_WIDTH:0] b_wptr; + wire [PTR_WIDTH:0] b_rptr; + wire [PTR_WIDTH:0] g_wptr; + wire [PTR_WIDTH:0] g_rptr; + wire [PTR_WIDTH:0] g_wptr_sync; + wire [PTR_WIDTH:0] g_rptr_sync; + wire full, empty; + + Sync2Flop #(PTR_WIDTH + 1) wptrSync (.clk(clk_w), .rst(rst), .in(g_wptr), .out(g_wptr_sync)); + Sync2Flop #(PTR_WIDTH + 1) rptrSync (.clk(clk_r), .rst(rst), .in(g_rptr), .out(g_rptr_sync)); + WptrHandler #(PTR_WIDTH) wptrHandler (.clk(clk_w), .rst(rst), .en(valid_w), .g_rptr_sync(g_rptr_sync), + .g_wptr(g_wptr), .b_wptr(b_wptr), .full(full)); + RptrHandler #(PTR_WIDTH) rptrHandler (.clk(clk_r), .rst(rst), .en(ready_r), .g_wptr_sync(g_wptr_sync), + .g_rptr(g_rptr), .b_rptr(b_rptr), .empty(empty)); + Fifo #(PTR_WIDTH, WIDTH) fifo (.rst(rst), + .clk_w(clk_w), .en_w(valid_w), .data_w(data_w), .b_wptr(b_wptr), .full(full), + .clk_r(clk_r), .en_r(ready_r), .data_r(data_r), .b_rptr(b_rptr), .empty(empty)); + assign valid_r = ~empty; + assign ready_w = ~full; + +endmodule + +module Sync2Flop #( + parameter PTR_WIDTH = 8 +)( + input clk, + input rst, + input [PTR_WIDTH-1:0] in, + output reg [PTR_WIDTH-1:0] out +); + reg [PTR_WIDTH-1:0] mid; + always_ff @(posedge clk, negedge rst) begin + if (~rst) begin + out <= '0; + mid <= '0; + end else begin + out <= mid; + mid <= in; + end + end +endmodule + +module WptrHandler #( + parameter PTR_WIDTH = 8 +)( + input clk, + input rst, + input en, + input [PTR_WIDTH:0] g_rptr_sync, + output reg [PTR_WIDTH:0] g_wptr, b_wptr, + output reg full +); + wire [PTR_WIDTH:0] g_wptr_next; + wire [PTR_WIDTH:0] b_wptr_next; + wire full_next; + + assign b_wptr_next = b_wptr + (en & ~full); + assign g_wptr_next = b_wptr_next ^ (b_wptr_next >> 1); + assign full_next = g_wptr_next == {~g_rptr_sync[PTR_WIDTH:PTR_WIDTH-1], g_rptr_sync[PTR_WIDTH-2:0]}; + + always_ff @(posedge clk, negedge rst) begin + if(~rst) begin + g_wptr <= '0; + b_wptr <= '0; + full <= '0; + // g_wptr <= g_wptr_next; + // b_wptr <= b_wptr_next; + // full <= full_next; + end else begin + g_wptr <= g_wptr_next; + b_wptr <= b_wptr_next; + full <= full_next; + end + end + +endmodule + +module RptrHandler #( + parameter PTR_WIDTH = 8 +)( + input clk, + input rst, + input en, + input [PTR_WIDTH:0] g_wptr_sync, + output reg [PTR_WIDTH:0] g_rptr, b_rptr, + output reg empty +); + wire [PTR_WIDTH:0] g_rptr_next; + wire [PTR_WIDTH:0] b_rptr_next; + wire empty_next; + + assign b_rptr_next = b_rptr + (en & ~empty); + assign g_rptr_next = b_rptr_next ^ (b_rptr_next >> 1); + assign empty_next = g_rptr_next == g_wptr_sync; + + always_ff @(posedge clk, negedge rst) begin + if(~rst) begin + g_rptr <= '0; + b_rptr <= '0; + empty <= '1; + end else begin + g_rptr <= g_rptr_next; + b_rptr <= b_rptr_next; + empty <= empty_next; + end + end + +endmodule + +module Fifo #( + parameter PTR_WIDTH = 8, + parameter WIDTH = 8 +)( + input rst, + // Write + input [WIDTH-1:0] data_w, + input clk_w, + input en_w, + input [PTR_WIDTH:0] b_wptr, + input full, + // Read + output reg [WIDTH-1:0] data_r, + input clk_r, + input en_r, + input [PTR_WIDTH:0] b_rptr, + input empty +); + localparam ENTRIES = 2**PTR_WIDTH; + integer i; + reg [WIDTH-1:0] fifoBank [0:ENTRIES-1]; + always_ff @(posedge clk_w, negedge rst) begin + if(~rst) begin + for(i = 0; i < ENTRIES; i++) begin + fifoBank[i] <= 'b0; + end + end else if (en_w & ~full) begin + fifoBank[b_wptr[PTR_WIDTH-1:0]] <= data_w; + end else begin + for(i = 0; i < ENTRIES; i++) begin + fifoBank[i] <= fifoBank[i]; + end + end + end + + // always_ff @(posedge clk_r) begin + // if(en_r & ~empty) begin + // data_r <= fifoBank[b_rptr[PTR_WIDTH-1:0]]; + // end + // end + + assign data_r = fifoBank[b_rptr[PTR_WIDTH-1:0]]; +endmodule + +module BinaryToGray #( + parameter WIDTH = 8 +)( + input [WIDTH-1:0] in, + output [WIDTH-1:0] out +); + +genvar i; +assign out[WIDTH-1] = in[WIDTH - 1]; +for(i = WIDTH - 2; i >= 0; i--) begin + assign out[i] = in[i] ^ in[i + 1]; +end + +endmodule + +module GrayToBinary #( + parameter WIDTH = 8 +)( + input [WIDTH-1:0] in, + output [WIDTH-1:0] out +); +genvar i; +assign out[WIDTH-1] = in[WIDTH - 1]; +for(i = WIDTH - 2; i >= 0; i--) begin + assign out[i] = ^(in >> i); +end +endmodule \ No newline at end of file diff --git a/src/main/resources/vsrc/ClockSelector.v b/src/main/resources/vsrc/ClockSelector.v new file mode 100644 index 00000000..7b8b2b53 --- /dev/null +++ b/src/main/resources/vsrc/ClockSelector.v @@ -0,0 +1,14 @@ +module ClockMux2 ( + input clocksIn_0, + input clocksIn_1, + input sel, + output clockOut +); + + // REPLACE ME WITH A CLOCK CELL IF DESIRED + + // XXX be careful with this! You can get really nasty short edges if you + // don't switch carefully + assign clockOut = sel ? clocksIn_1 : clocksIn_0; + +endmodule \ No newline at end of file diff --git a/src/main/resources/vsrc/SBDeserializer.v b/src/main/resources/vsrc/SBDeserializer.v new file mode 100644 index 00000000..d27bd52b --- /dev/null +++ b/src/main/resources/vsrc/SBDeserializer.v @@ -0,0 +1,45 @@ +module SBDeserializerBlackBox #( + parameter WIDTH = 128, + parameter WIDTH_W = $clog2(WIDTH) +) ( + input clk, + input rst, + input in_data, + output [WIDTH - 1:0] out_data, + output out_data_valid + +); + +reg [WIDTH_W-1:0] counter; +reg [WIDTH-1:0] data_reg; +reg receiving; +wire recvDone; + +assign out_data = data_reg; +assign recvDone = counter == (WIDTH - 1); +assign out_data_valid = !receiving; + +always @(negedge clk or posedge rst) begin + if (rst) begin + counter <= 0; + receiving <= 1'b1; + end else begin + if (recvDone) begin + counter <= 0; + receiving <= 1'b0; + end else begin + counter <= counter + 1'b1; + receiving <= 1'b1; + end + + // if (out_data_valid && out_data_ready) begin + // receiving <= 1'b1; + // end + + data_reg[counter] <= in_data; + + end + +end + +endmodule \ No newline at end of file diff --git a/src/main/resources/vsrc/SBSerializer.v b/src/main/resources/vsrc/SBSerializer.v new file mode 100644 index 00000000..b1e83222 --- /dev/null +++ b/src/main/resources/vsrc/SBSerializer.v @@ -0,0 +1,13 @@ +module SBSerializerBlackBox #( + parameter WIDTH = 128, + parameter WIDTH_W = $clog2(WIDTH) +) ( + + input clk, + input rst, + input [WIDTH - 1:0] in_data, + output out_data + + ); + +endmodule \ No newline at end of file diff --git a/src/main/scala/DummyModule.scala b/src/main/scala/DummyModule.scala index 8571b870..8df761d4 100644 --- a/src/main/scala/DummyModule.scala +++ b/src/main/scala/DummyModule.scala @@ -8,5 +8,6 @@ class DummyModule extends Module { val b = Output(Bool()) }) + io.b := ~io.a } diff --git a/src/main/scala/d2dadapter/D2DSidebandModule.scala b/src/main/scala/d2dadapter/D2DSidebandModule.scala index 3f3ccac6..55fd0cce 100644 --- a/src/main/scala/d2dadapter/D2DSidebandModule.scala +++ b/src/main/scala/d2dadapter/D2DSidebandModule.scala @@ -65,39 +65,39 @@ class D2DSidebandModule(val fdiParams: FdiParams, val sbParams: SidebandParams) sideband_switch.io.inner.layer_to_node_above.bits := 0.U(sbParams.sbNodeMsgWidth.W) sideband_switch.io.inner.layer_to_node_above.valid := false.B - sideband_switch.io.inner.node_to_layer_below.ready := false.B + sideband_switch.io.inner.node_to_layer_below.ready := true.B sideband_switch.io.inner.node_to_layer_above.ready := true.B - when(sideband_switch.io.inner.node_to_layer_above.valid && sideband_switch.io.inner.node_to_layer_above.ready){ - when(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_REQ_ACTIVE){ + when(sideband_switch.io.inner.node_to_layer_below.valid && sideband_switch.io.inner.node_to_layer_below.ready){ + when(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_REQ_ACTIVE){ io.sideband_rcv := SideBandMessage.REQ_ACTIVE - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_REQ_L1){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_REQ_L1){ io.sideband_rcv := SideBandMessage.REQ_L1 - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_REQ_L2){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_REQ_L2){ io.sideband_rcv := SideBandMessage.REQ_L2 - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_REQ_LINK_RESET){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_REQ_LINK_RESET){ io.sideband_rcv := SideBandMessage.REQ_LINKRESET - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_REQ_DISABLE){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_REQ_DISABLE){ io.sideband_rcv := SideBandMessage.REQ_DISABLED - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_RSP_ACTIVE){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_RSP_ACTIVE){ io.sideband_rcv := SideBandMessage.RSP_ACTIVE - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_RSP_PM_NAK){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_RSP_PM_NAK){ io.sideband_rcv := SideBandMessage.RSP_PMNAK - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_RSP_L1){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_RSP_L1){ io.sideband_rcv := SideBandMessage.RSP_L1 - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_RSP_L2){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_RSP_L2){ io.sideband_rcv := SideBandMessage.RSP_L2 - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_RSP_LINK_RESET){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_RSP_LINK_RESET){ io.sideband_rcv := SideBandMessage.RSP_LINKRESET - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.LINK_MGMT_ADAPTER0_RSP_DISABLE){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.LINK_MGMT_ADAPTER0_RSP_DISABLE){ io.sideband_rcv := SideBandMessage.RSP_DISABLED - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.PARITY_FEATURE_REQ){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.PARITY_FEATURE_REQ){ io.sideband_rcv := SideBandMessage.PARITY_FEATURE_REQ - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.PARITY_FEATURE_ACK){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.PARITY_FEATURE_ACK){ io.sideband_rcv := SideBandMessage.PARITY_FEATURE_ACK - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.PARITY_FEATURE_NAK){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.PARITY_FEATURE_NAK){ io.sideband_rcv := SideBandMessage.PARITY_FEATURE_NAK - }.elsewhen(sideband_switch.io.inner.node_to_layer_above.bits === SBM.ADV_CAP){ + }.elsewhen(sideband_switch.io.inner.node_to_layer_below.bits === SBM.ADV_CAP){ io.sideband_rcv := SideBandMessage.ADV_CAP }.otherwise{ io.sideband_rcv := SideBandMessage.NOP diff --git a/src/main/scala/d2dadapter/LinkInitSubmodule.scala b/src/main/scala/d2dadapter/LinkInitSubmodule.scala index c5b443a3..0dee3a57 100644 --- a/src/main/scala/d2dadapter/LinkInitSubmodule.scala +++ b/src/main/scala/d2dadapter/LinkInitSubmodule.scala @@ -195,6 +195,14 @@ class LinkInitSubmodule() extends Module { linkinit_state_reg := LinkInitState.INIT_DONE } } + }.elsewhen(io.link_state === PhyState.active){ + io.active_entry := true.B + io.linkinit_fdi_pl_state_sts := PhyState.active + io.linkinit_fdi_pl_rxactive_req := true.B + io.linkinit_fdi_pl_inband_pres := true.B + io.linkinit_rdi_lp_state_req := PhyStateReq.active + io.linkinit_sb_snd := SideBandMessage.NOP + linkinit_state_reg := LinkInitState.INIT_DONE }.otherwise{ linkinit_state_reg := LinkInitState.INIT_START io.active_entry := false.B diff --git a/src/main/scala/e2e/UCITop.scala b/src/main/scala/e2e/UCITop.scala index f8058d04..489ca75a 100644 --- a/src/main/scala/e2e/UCITop.scala +++ b/src/main/scala/e2e/UCITop.scala @@ -4,53 +4,70 @@ package e2e import chisel3._ import chisel3.util._ +import afe._ import interfaces._ import sideband._ import protocol._ import d2dadapter._ import logphy._ +import afe._ import freechips.rocketchip.util.AsyncQueueParams -/** - * UCITop is the main class which instantiates all the three - * layers of the UCIe protocol stack - * +/** UCITop is the main class which instantiates all the three layers of the UCIe + * protocol stack */ -class UCITop(val fdiParams: FdiParams, val rdiParams: RdiParams, - val sbParams: SidebandParams, val myId: BigInt, - val linkTrainingParams: LinkTrainingParams, - val afeParams: AfeParams, - val laneAsyncQueueParams: AsyncQueueParams) extends Module { - val io = IO(new Bundle{ - // IOs for connecting to the protocol layer - //val fdi = new Fdi(fdiParams) - val fdi_lpConfig = Valid(Bits(fdiParams.sbWidth.W)) - val fdi_lpConfigCredit = Input(Bool()) - val fdi_plConfig = Flipped(Valid(Bits(fdiParams.sbWidth.W))) - val fdi_plConfigCredit = Output(Bool()) - val fdi_lpStallAck = Output(Bool()) - val TLplStateStatus = Output(PhyState()) - - val TLlpData_valid = Input(Bool()) - val TLlpData_bits = Input(Bits((8 * fdiParams.width).W)) - val TLlpData_irdy = Input(Bool()) - val TLlpData_ready = Output(Bool()) - val TLplData_bits = Output(Bits((8 * fdiParams.width).W)) - val TLplData_valid = Output(Bool()) - val TLready_to_rcv = Input(Bool()) - val fault = Input(Bool()) - val soft_reset = Input(Bool()) - // IOs for connecting to the AFE - val mbAfe = new MainbandAfeIo(afeParams) - val sbAfe = new SidebandAfeIo(afeParams) - }) +class UCITop( + val fdiParams: FdiParams, + val rdiParams: RdiParams, + val sbParams: SidebandParams, + val linkTrainingParams: LinkTrainingParams, + val afeParams: AfeParams, + val laneAsyncQueueParams: AsyncQueueParams, +) extends Module { + val io = IO(new Bundle { + // IOs for connecting to the protocol layer + // val fdi = new Fdi(fdiParams) + val fdi_lpConfig = Valid(Bits(fdiParams.sbWidth.W)) + val fdi_lpConfigCredit = Input(Bool()) + val fdi_plConfig = Flipped(Valid(Bits(fdiParams.sbWidth.W))) + val fdi_plConfigCredit = Output(Bool()) + val fdi_lpStallAck = Output(Bool()) + val TLplStateStatus = Output(PhyState()) + val TLlpData_valid = Input(Bool()) + val TLlpData_bits = Input(Bits((8 * fdiParams.width).W)) + val TLlpData_irdy = Input(Bool()) + val TLlpData_ready = Output(Bool()) + val TLplData_bits = Output(Bits((8 * fdiParams.width).W)) + val TLplData_valid = Output(Bool()) + val TLready_to_rcv = Input(Bool()) + val fault = Input(Bool()) + val soft_reset = Input(Bool()) + // IOs for connecting to the AFE in the standalone mode + val mbAfe_tx = if (afeParams.STANDALONE) Some(Output(new MainbandIo(afeParams.mbLanes))) else None + val mbAfe_rx = if (afeParams.STANDALONE) Some(Input(new MainbandIo(afeParams.mbLanes))) else None + val phyAfe = if (afeParams.STANDALONE) None else Some(new MainbandLaneIOWithValid(afeParams)) + val sbTxIO = Output(new SidebandIo) + val sbRxIO = Input(new SidebandIo) + val train = if (afeParams.STANDALONE) None else Some(new TrainingOperation(afeParams, linkTrainingParams.maxPatternCount)) + }) // Instantiate the agnostic protocol layer val protocol = Module(new ProtocolLayer(fdiParams)) // Instantiate the D2D adapter val d2dadapter = Module(new D2DAdapter(fdiParams, rdiParams, sbParams)) // Instantiate the logPhy - val logPhy = Module(new LogicalPhy(myId, linkTrainingParams, afeParams, rdiParams, fdiParams, sbParams, laneAsyncQueueParams)) + val logPhy = Module( + new LogicalPhy( + linkTrainingParams, + afeParams, + rdiParams, + fdiParams, + sbParams, + laneAsyncQueueParams, + ), + ) + + val dafe = Module(new MbAfe(afeParams)) // Connect the FDI interface of Protocol layer to D2D adapter protocol.io.fdi <> d2dadapter.io.fdi @@ -58,19 +75,48 @@ class UCITop(val fdiParams: FdiParams, val rdiParams: RdiParams, // Connect the RDI interface of D2D adapter to logPhy d2dadapter.io.rdi <> logPhy.io.rdi - // Connect the AFE interface from logPhy to the top - io.mbAfe <> logPhy.io.mbAfe - io.sbAfe <> logPhy.io.sbAfe + /** Sideband AFE connections (ser/des in logphy) + */ + io.sbTxIO.clk := logPhy.io.sbAfe.txClock + io.sbTxIO.data := logPhy.io.sbAfe.txData + logPhy.io.sbAfe.pllLock := true.B + logPhy.io.sbAfe.rxClock := io.sbRxIO.clk + logPhy.io.sbAfe.rxData := io.sbRxIO.data + + /** Mainband AFE connections to toplevel IOs + */ + if (afeParams.STANDALONE) { io.mbAfe_tx.get <> dafe.io.mbTxData } + if (afeParams.STANDALONE) { + io.mbAfe_rx.get <> dafe.io.mbRxData + } else { + dafe.io.mbRxData := 0.U.asTypeOf(dafe.io.mbRxData) + } - // Connect the protocol IOs to the top for connections to the tilelink interface - //io.fdi <> protocol.io.fdi + /** Logphy connections to Digital AFE + */ + if (afeParams.STANDALONE) { + logPhy.io.mbAfe.get <> dafe.io.mbAfeIo + } else { + logPhy.io.phyAfe.get <> io.phyAfe.get + logPhy.io.train.get <> io.train.get + // defaults to zero + dafe.io.mbAfeIo.txData.valid := 0.U + dafe.io.mbAfeIo.txData.bits := 0.U.asTypeOf(dafe.io.mbAfeIo.txData.bits) + dafe.io.mbAfeIo.rxData.ready := 0.U + dafe.io.mbAfeIo.txFreqSel := 0.U.asTypeOf(dafe.io.mbAfeIo.txFreqSel) + dafe.io.mbAfeIo.rxEn := 0.U + } + + /* Connect the protocol IOs to the top for connections to the tilelink + * interface */ + // io.fdi <> protocol.io.fdi io.fdi_lpConfig <> protocol.io.fdi.lpConfig io.fdi_lpConfigCredit <> protocol.io.fdi.lpConfigCredit io.fdi_plConfig <> protocol.io.fdi.plConfig io.fdi_plConfigCredit <> protocol.io.fdi.plConfigCredit io.fdi_lpStallAck <> protocol.io.fdi.lpStallAck io.TLplStateStatus <> protocol.io.TLplStateStatus - + protocol.io.TLlpData_valid := io.TLlpData_valid protocol.io.TLlpData_bits := io.TLlpData_bits protocol.io.TLlpData_irdy := io.TLlpData_irdy @@ -81,4 +127,4 @@ class UCITop(val fdiParams: FdiParams, val rdiParams: RdiParams, protocol.io.fault := io.fault protocol.io.soft_reset := io.soft_reset -} \ No newline at end of file +} diff --git a/src/main/scala/interfaces/Afe.scala b/src/main/scala/interfaces/Afe.scala index 88b62cec..6a1e256f 100644 --- a/src/main/scala/interfaces/Afe.scala +++ b/src/main/scala/interfaces/Afe.scala @@ -25,7 +25,7 @@ class MainbandIo(lanes: Int = 16) extends Bundle { */ class SidebandIo extends Bundle { val data = Bool() - val clk = Clock() + val clk = Bool() } /** The pins (mainband and sideband) exposed by a standard package UCIe module @@ -49,6 +49,7 @@ case class AfeParams( sbWidth: Int = 1, mbSerializerRatio: Int = 16, mbLanes: Int = 16, + STANDALONE: Boolean = true ) /** The sideband analog front-end (AFE) interface, from the perspective of the @@ -62,8 +63,6 @@ class SidebandAfeIo( afeParams: AfeParams, ) extends Bundle { - val fifoParams = Input(new FifoParams()) - /** Data to transmit on the sideband. * * Output from the async FIFO. diff --git a/src/main/scala/logphy/DataWidthCoupler.scala b/src/main/scala/logphy/DataWidthCoupler.scala index 0b6aef31..93b1bb9a 100644 --- a/src/main/scala/logphy/DataWidthCoupler.scala +++ b/src/main/scala/logphy/DataWidthCoupler.scala @@ -3,17 +3,39 @@ package logphy import chisel3._ import chisel3.util._ +import chisel3.experimental.BundleLiterals._ +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor case class DataWidthCouplerParams( val inWidth: Int = 4, val outWidth: Int = 4, ) +class WidthCoupleData( + params: DataWidthCouplerParams, +) extends Bundle { + val data = UInt(params.outWidth.W) +} + +class DataWithValid( + params: DataWidthCouplerParams, +) extends WidthCoupleData(params) { + assert(params.outWidth > params.inWidth) + val valid = Vec(params.outWidth, Bool()) +} + class DataWidthCouplerIO( params: DataWidthCouplerParams, ) extends Bundle { val in = Flipped(Decoupled(UInt(params.inWidth.W))) - val out = Decoupled(UInt(params.outWidth.W)) + val out = Decoupled(new WidthCoupleData(params)) +} + +class DataWidthValidIO( + params: DataWidthCouplerParams, +) extends DataWidthCouplerIO(params) { + override val in = Flipped(Decoupled(UInt(params.inWidth.W))) + override val out = Decoupled(new DataWithValid(params)) } class DataWidthCoupler(params: DataWidthCouplerParams) extends Module { @@ -49,9 +71,9 @@ class DataWidthCoupler(params: DataWidthCouplerParams) extends Module { } } is(State.CHUNK_OR_COLLECT) { - io.out.bits := inData + io.out.bits.data := inData .asTypeOf(Vec(ratio, Bits(params.outWidth.W)))( - (ratio - 1).U - chunkCounter, + chunkCounter, ) io.out.valid := true.B when(io.out.fire) { @@ -86,7 +108,7 @@ class DataWidthCoupler(params: DataWidthCouplerParams) extends Module { is(State.IDLE) { io.in.ready := true.B when(io.in.fire) { - inData((ratio - 1).U - inSliceCounter) := io.in.bits + inData(inSliceCounter) := io.in.bits inSliceCounter := inSliceCounter + 1.U } when(inSliceCounter === (ratio - 1).U) { @@ -106,3 +128,113 @@ class DataWidthCoupler(params: DataWidthCouplerParams) extends Module { } } + +class DataWidthValidFramer(params: DataWidthCouplerParams) extends Module { + + val io = IO( + new DataWidthValidIO(params), + ) + + private object State extends ChiselEnum { + val IDLE, COLLECT, FULL = Value + } + + private val currentState = RegInit(State.IDLE) + io.out.noenq() + io.in.nodeq() + + assert( + params.outWidth % params.inWidth == 0, + "params.outWidth must be a multiple of params.inWidth", + ) + + assert( + params.outWidth > params.inWidth, + "params.outWidth must be greater than in width for valid framing", + ) + + val ratio = params.outWidth / params.inWidth + + /** need to collect incoming message */ + + private class DataValid extends Bundle { + val data = UInt(params.inWidth.W) + val valid = Vec(params.inWidth, Bool()) + } + + val inSliceCounter = RegInit(0.U(log2Ceil(ratio).W)) + private val invalidData = (new DataValid()).Lit( + _.valid -> Vec.Lit(Seq.fill(params.inWidth)(false.B): _*), + _.data -> 0.U, + ) + + private val inDataWire = Wire(Vec(ratio, new DataValid())) + private val inData = + RegInit( + VecInit( + Seq.fill(ratio)(invalidData), + ), + ) + inDataWire := inData + inData := inDataWire + + printf(cf"inSliceCounter: $inSliceCounter\n") + printf(cf"currentState: $currentState\n") + printf(cf"io.out.bits.valid: ${io.out.bits.valid}\n") + + io.out.bits.data := VecInit(inDataWire.map(_.data)).asUInt + io.out.bits.valid := VecInit(inDataWire.map(_.valid)) + .asTypeOf(io.out.bits.valid) + switch(currentState) { + is(State.IDLE) { + io.in.ready := true.B + when(io.in.fire) { + val newData = Wire(new DataValid()) + newData.data := io.in.bits + newData.valid := Vec.Lit( + Seq.fill(params.inWidth)(true.B): _*, + ) + + inDataWire(0) := newData + io.out.valid := true.B + when(!io.out.fire) { + inSliceCounter := 1.U + currentState := State.COLLECT + }.otherwise { + inData := VecInit(Seq.fill(ratio)(invalidData)) + } + } + } + is(State.COLLECT) { + io.out.valid := true.B + io.in.ready := true.B + when(io.in.fire) { + val newData = Wire(new DataValid()) + newData.data := io.in.bits + newData.valid := Vec.Lit( + Seq.fill(params.inWidth)(true.B): _*, + ) + + inDataWire(inSliceCounter) := newData + inSliceCounter := inSliceCounter + 1.U + when(inSliceCounter === (ratio - 1).U) { + inSliceCounter := 0.U + currentState := State.FULL + } + } + when(io.out.fire) { + inData := VecInit(Seq.fill(ratio)(invalidData)) + currentState := State.IDLE + } + printf(cf"inDataWire: ${inDataWire}\n") + } + is(State.FULL) { + io.out.valid := true.B + when(io.out.fire) { + inData := VecInit(Seq.fill(ratio)(invalidData)) + currentState := State.IDLE + } + } + } + +} diff --git a/src/main/scala/logphy/ErrorCounter.scala b/src/main/scala/logphy/ErrorCounter.scala new file mode 100644 index 00000000..508f8937 --- /dev/null +++ b/src/main/scala/logphy/ErrorCounter.scala @@ -0,0 +1,81 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chisel3.util._ +import interfaces.AfeParams + +/** TODO: need to do per-lane, not just aggregate */ +class ErrorCounter(afeParams: AfeParams) extends Module { + + val io = IO(new Bundle { + val req = Flipped(Valid(new Bundle { + val pattern = TransmitPattern() + val input = Input( + Vec(afeParams.mbLanes, UInt(afeParams.mbSerializerRatio.W)), + ) + })) + val errorCount = Output( + Vec(afeParams.mbLanes, UInt(log2Ceil(afeParams.mbSerializerRatio + 1).W)), + ) + }) + + val lfsr = Module( + new UCIeScrambler(afeParams = afeParams, numLanes = afeParams.mbLanes), + ) + + lfsr.io.valid := io.req.valid && io.req.bits.pattern === TransmitPattern.LFSR + lfsr.io.data_in := VecInit( + Seq.fill(afeParams.mbLanes)(0.U(afeParams.mbSerializerRatio.W)), + ) + + val expected = WireInit( + VecInit( + Seq.fill(afeParams.mbLanes)(0.U(afeParams.mbSerializerRatio.W)), + ), + ) + + /** Assign expected value */ + switch(io.req.bits.pattern) { + is(TransmitPattern.CLOCK) { + assert(!io.req.valid, "Cannot do error count with sideband clock pattern") + } + is(TransmitPattern.LFSR) { + expected := lfsr.io.data_out + } + is(TransmitPattern.PER_LANE_ID) { + val perLaneId = VecInit(Seq.fill(afeParams.mbLanes)(0.U(16.W))) + for (i <- 0 until afeParams.mbLanes) { + perLaneId(i) := Cat("b1010".U(4.W), i.U(8.W), "b1010".U(4.W)) + } + val ratio16 = afeParams.mbSerializerRatio / 16 + val patternVec = VecInit.tabulate(afeParams.mbLanes, ratio16) { (_, _) => + 0.U(16.W) + } + for (i <- 0 until afeParams.mbLanes) { + for (j <- 0 until ratio16) { + patternVec(i)(j) := perLaneId(i) + } + } + expected := patternVec.asTypeOf(expected) + } + is(TransmitPattern.VALTRAIN) { + val valtrain = VecInit( + Seq.fill(afeParams.mbLanes * afeParams.mbSerializerRatio / 8)( + "b1111_0000".U(8.W), + ), + ) + expected := valtrain.asTypeOf(expected) + } + } + + /** count errors */ + val diffVec = Wire( + Vec(afeParams.mbLanes, Vec(afeParams.mbSerializerRatio, UInt(1.W))), + ) + for (i <- 0 until afeParams.mbLanes) { + diffVec(i) := (expected(i) ^ io.req.bits.input(i)).asTypeOf(diffVec(i)) + io.errorCount(i) := diffVec(i).reduceTree(_ +& _) + } + +} diff --git a/src/main/scala/logphy/Lanes.scala b/src/main/scala/logphy/Lanes.scala index 7be48ce3..65808457 100644 --- a/src/main/scala/logphy/Lanes.scala +++ b/src/main/scala/logphy/Lanes.scala @@ -5,15 +5,139 @@ import interfaces._ import chisel3._ import chisel3.util._ import freechips.rocketchip.util.{AsyncQueue, AsyncQueueParams} +import logphy.Scrambler + +object LanesToOne { + def apply( + laneData: Vec[UInt], + numLanes: Int, + serializerRatio: Int, + ): Bits = { + val ratioBytes = serializerRatio / 8 + val rxDataVec = Wire( + Vec(ratioBytes, Vec(numLanes, UInt(8.W))), + ) + for (i <- 0 until numLanes) { + for (j <- 0 until ratioBytes) { + rxDataVec(j)(i) := laneData(i).asTypeOf( + VecInit(Seq.fill(ratioBytes)(0.U(8.W))), + )(j) + } + } + rxDataVec.asUInt + } +} + +object OneToLanes { + def apply( + bits: Bits, + numLanes: Int, + serializerRatio: Int, + ): Vec[UInt] = { + val ratioBytes = serializerRatio / 8 + val result = Wire(Vec(numLanes, UInt(serializerRatio.W))) + val txDataVec = Wire(Vec(numLanes, Vec(ratioBytes, UInt(8.W)))) + val txDataBytes = Wire(Vec(numLanes * ratioBytes, UInt(8.W))) + txDataBytes := bits.asTypeOf(txDataBytes) + for (i <- 0 until numLanes) { + for (j <- 0 until ratioBytes) { + txDataVec(i)(j) := txDataBytes(numLanes * j + i) + } + } + for (i <- 0 until numLanes) { + result(i) := txDataVec(i).asUInt + } + result + } +} + +class LaneIO(afeParams: AfeParams) extends Bundle { + val scramble = Input(Bool()) + val mainbandLaneIO = new MainbandLaneIO(afeParams) + val mainbandIO = new MainbandIO(afeParams) +} + +abstract class LanesModule(afeParams: AfeParams) extends Module { + val io = IO(new LaneIO(afeParams)) + + val rxScrambler = + Module( + new UCIeScrambler(afeParams = afeParams, numLanes = afeParams.mbLanes), + ) + + val txScrambler = + Module( + new UCIeScrambler(afeParams = afeParams, numLanes = afeParams.mbLanes), + ) + + val rxScramblerInput = Wire(chiselTypeOf(io.mainbandLaneIO.rxData)) + val txScramblerOutput = Wire(chiselTypeOf(io.mainbandLaneIO.txData)) + + val scrambledTx = Wire(chiselTypeOf(io.mainbandLaneIO.txData.bits)) + val descrambledRx = Wire(chiselTypeOf(io.mainbandLaneIO.rxData.bits)) + val rxDataInput = Wire(chiselTypeOf(io.mainbandLaneIO.rxData.bits)) + + rxScrambler.io.data_in := rxScramblerInput.bits + rxScrambler.io.valid := rxScramblerInput.fire + descrambledRx := rxScrambler.io.data_out + + rxDataInput := Mux( + io.scramble, + rxScrambler.io.data_out, + rxScramblerInput.bits, + ) + + io.mainbandIO.rxData.bits := LanesToOne( + rxDataInput, + afeParams.mbLanes, + afeParams.mbSerializerRatio, + ) + io.mainbandIO.rxData.valid := rxScramblerInput.valid + rxScramblerInput.ready := true.B + + scrambledTx := txScrambler.io.data_out + txScrambler.io.data_in := OneToLanes( + io.mainbandIO.txData.bits, + afeParams.mbLanes, + afeParams.mbSerializerRatio, + ) + txScrambler.io.valid := io.mainbandIO.txData.fire + + txScramblerOutput.valid := io.mainbandIO.txData.valid + io.mainbandIO.txData.ready := txScramblerOutput.ready + txScramblerOutput.bits := Mux( + io.scramble, + scrambledTx, + txScrambler.io.data_in, + ) + +} + +class LanesNoFifo( + afeParams: AfeParams, +) extends LanesModule(afeParams) { + + assert( + afeParams.mbSerializerRatio > 8 && afeParams.mbSerializerRatio % 8 == 0, + ) + + if (afeParams.mbSerializerRatio == 16) { + rxScramblerInput <> io.mainbandLaneIO.rxData + io.mainbandLaneIO.txData <> txScramblerOutput + } else { + rxScramblerInput <> io.mainbandLaneIO.rxData + io.mainbandLaneIO.txData <> txScramblerOutput + } + + +} class Lanes( afeParams: AfeParams, queueParams: AsyncQueueParams, -) extends Module { - val io = IO(new Bundle() { - val mainbandIo = new MainbandIO(afeParams) - val mainbandLaneIO = new MainbandLaneIO(afeParams) - }) +) extends LanesModule(afeParams) { + + val asyncQueueIO = IO(Input(new FifoParams)) val txMBFifo = Module( @@ -30,64 +154,30 @@ class Lanes( ), ) - rxMBFifo.io.enq <> io.mainbandIo.rxData + rxMBFifo.io.enq <> io.mainbandLaneIO.rxData rxMBFifo.io.deq_clock := clock rxMBFifo.io.deq_reset := reset - rxMBFifo.io.enq_clock := io.mainbandIo.fifoParams.clk - rxMBFifo.io.enq_reset := io.mainbandIo.fifoParams.reset - txMBFifo.io.deq <> io.mainbandIo.txData + rxMBFifo.io.enq_clock := asyncQueueIO.clk + rxMBFifo.io.enq_reset := asyncQueueIO.reset + txMBFifo.io.deq <> io.mainbandLaneIO.txData txMBFifo.io.enq_clock := clock txMBFifo.io.enq_reset := reset - txMBFifo.io.deq_clock := io.mainbandIo.fifoParams.clk - txMBFifo.io.deq_reset := io.mainbandIo.fifoParams.reset + txMBFifo.io.deq_clock := asyncQueueIO.clk + txMBFifo.io.deq_reset := asyncQueueIO.reset - txMBFifo.io.enq.valid := io.mainbandLaneIO.txData.valid - io.mainbandLaneIO.rxData.valid := rxMBFifo.io.deq.valid assert( afeParams.mbSerializerRatio > 8 && afeParams.mbSerializerRatio % 8 == 0, ) - val txDataVec = Wire( - Vec(afeParams.mbLanes, Vec(afeParams.mbSerializerRatio / 8, UInt(8.W))), - ) - val ratioBytes = afeParams.mbSerializerRatio / 8 - val rxDataVec = Wire( - Vec(ratioBytes, Vec(afeParams.mbLanes, UInt(8.W))), - ) - for (i <- 0 until afeParams.mbLanes) { - for (j <- 0 until ratioBytes) { - txDataVec(afeParams.mbLanes - 1 - i)(j) := io.mainbandLaneIO.txData - .bits( - afeParams.mbLanes * 8 * j + (i * 8) + 7, - afeParams.mbLanes * 8 * j + (i * 8), - ) - rxDataVec(j)(afeParams.mbLanes - 1 - i) := rxMBFifo.io.deq - .bits(i)((j + 1) * 8 - 1, j * 8) - } - txMBFifo.io.enq.bits(i) := txDataVec(i).asUInt - } - io.mainbandLaneIO.rxData.bits := rxDataVec.asUInt - rxMBFifo.io.deq.ready := true.B - io.mainbandLaneIO.txData.ready := txMBFifo.io.enq.ready -} -class MainbandSimIO(afeParams: AfeParams) extends Bundle { - val txData = Decoupled( - Vec(afeParams.mbLanes, Bits(afeParams.mbSerializerRatio.W)), - ) - val rxData = Flipped( - Decoupled(Vec(afeParams.mbLanes, Bits(afeParams.mbSerializerRatio.W))), - ) + rxScramblerInput <> rxMBFifo.io.deq + txMBFifo.io.enq <> txScramblerOutput + } class SimLanes( afeParams: AfeParams, queueParams: AsyncQueueParams, -) extends Module { - - val io = IO(new Bundle() { - val mainbandIo = new MainbandSimIO(afeParams) - val mainbandLaneIO = new MainbandLaneIO(afeParams) - }) +) extends LanesModule(afeParams) { val txMBFifo = Module( @@ -104,35 +194,13 @@ class SimLanes( ), ) - rxMBFifo.io.enq <> io.mainbandIo.rxData - txMBFifo.io.deq <> io.mainbandIo.txData + rxMBFifo.io.enq <> io.mainbandLaneIO.rxData + txMBFifo.io.deq <> io.mainbandLaneIO.txData - txMBFifo.io.enq.valid := io.mainbandLaneIO.txData.valid - io.mainbandLaneIO.rxData.valid := rxMBFifo.io.deq.valid assert( afeParams.mbSerializerRatio > 8 && afeParams.mbSerializerRatio % 8 == 0, ) - val txDataVec = Wire( - Vec(afeParams.mbLanes, Vec(afeParams.mbSerializerRatio / 8, UInt(8.W))), - ) - val ratioBytes = afeParams.mbSerializerRatio / 8 - val rxDataVec = Wire( - Vec(ratioBytes, Vec(afeParams.mbLanes, UInt(8.W))), - ) - for (i <- 0 until afeParams.mbLanes) { - for (j <- 0 until ratioBytes) { - txDataVec(afeParams.mbLanes - 1 - i)(j) := io.mainbandLaneIO.txData - .bits( - afeParams.mbLanes * 8 * j + (i * 8) + 7, - afeParams.mbLanes * 8 * j + (i * 8), - ) - rxDataVec(j)(afeParams.mbLanes - 1 - i) := rxMBFifo.io.deq - .bits(i)((j + 1) * 8 - 1, j * 8) - } - txMBFifo.io.enq.bits(i) := txDataVec(i).asUInt - } - io.mainbandLaneIO.rxData.bits := rxDataVec.asUInt - rxMBFifo.io.deq.ready := true.B - io.mainbandLaneIO.txData.ready := txMBFifo.io.enq.ready + rxScramblerInput <> rxMBFifo.io.deq + txMBFifo.io.enq <> txScramblerOutput } diff --git a/src/main/scala/logphy/LinkTrainingFSM.scala b/src/main/scala/logphy/LinkTrainingFSM.scala index 699c1eba..1773b02b 100644 --- a/src/main/scala/logphy/LinkTrainingFSM.scala +++ b/src/main/scala/logphy/LinkTrainingFSM.scala @@ -7,9 +7,8 @@ import chisel3._ import chisel3.util._ /** Implementation TODOs: - * - investigate multiple message source issue * - implement plStallReq - * - implement lpStateReq + * - retrain state */ case class LinkTrainingParams( @@ -18,6 +17,7 @@ case class LinkTrainingParams( maxSBMessageSize: Int = 128, mbTrainingParams: MBTrainingParams = MBTrainingParams(), sbClockFreqAnalog: Int = 800_000_000, + maxPatternCount: Int = 1 << 32, ) class SidebandFSMIO( @@ -33,14 +33,14 @@ class SidebandFSMIO( val rxMode = Input(RXTXMode()) val txMode = Input(RXTXMode()) val rxEn = Input(Bool()) - val pllLock = Output(Bool()) } class MainbandFSMIO( + afeParams: AfeParams, ) extends Bundle { val rxEn = Input(Bool()) - val pllLock = Output(Bool()) val txFreqSel = Input(SpeedMode()) + val mainbandIO = new MainbandIO(afeParams) } class LinkTrainingFSM( @@ -53,33 +53,46 @@ class LinkTrainingFSM( linkTrainingParams.sbClockFreqAnalog / afeParams.sbSerializerRatio val io = IO(new Bundle { - val mainbandFSMIO = Flipped(new MainbandFSMIO) + val mainbandFSMIO = Flipped(new MainbandFSMIO(afeParams)) val sidebandFSMIO = Flipped(new SidebandFSMIO(sbParams)) val rdi = new Bundle { val rdiBringupIO = new RdiBringupIO } + val trainingOperationIO = + new TrainingOperation(afeParams, linkTrainingParams.maxPatternCount) val currentState = Output(LinkTrainingState()) }) - val patternGenerator = Module(new PatternGenerator(afeParams, sbParams)) + val patternGenerator = Module( + new PatternGenerator(afeParams, sbParams, maxPatternCount = 1024), + ) val sbMsgWrapper = Module(new SBMsgWrapper(sbParams)) private val msgSource = WireInit(MsgSource.PATTERN_GENERATOR) - // io.mainbandLaneIO <> patternGenerator.io.mainbandLaneIO + io.mainbandFSMIO.mainbandIO <> patternGenerator.io.mainbandIO patternGenerator.io.patternGeneratorIO.transmitReq.noenq() - patternGenerator.io.patternGeneratorIO.transmitPatternStatus.nodeq() + patternGenerator.io.patternGeneratorIO.resp.nodeq() sbMsgWrapper.io.trainIO.msgReq.noenq() sbMsgWrapper.io.trainIO.msgReqStatus.nodeq() - io.sidebandFSMIO.patternTxData <> patternGenerator.io.sidebandLaneIO.txData - io.sidebandFSMIO.packetTxData <> sbMsgWrapper.io.laneIO.txData when(msgSource === MsgSource.PATTERN_GENERATOR) { io.sidebandFSMIO.rxData <> patternGenerator.io.sidebandLaneIO.rxData sbMsgWrapper.io.laneIO.rxData.noenq() + sbMsgWrapper.io.laneIO.txData.nodeq() + io.sidebandFSMIO.patternTxData <> patternGenerator.io.sidebandLaneIO.txData + io.sidebandFSMIO.packetTxData.noenq() }.otherwise { io.sidebandFSMIO.rxData <> sbMsgWrapper.io.laneIO.rxData patternGenerator.io.sidebandLaneIO.rxData.noenq() + patternGenerator.io.sidebandLaneIO.txData.nodeq() + when(io.sidebandFSMIO.rxMode === RXTXMode.RAW) { + io.sidebandFSMIO.patternTxData <> sbMsgWrapper.io.laneIO.txData + io.sidebandFSMIO.packetTxData.noenq() + }.otherwise { + io.sidebandFSMIO.patternTxData.noenq() + io.sidebandFSMIO.packetTxData <> sbMsgWrapper.io.laneIO.txData + } } private val currentState = RegInit(LinkTrainingState.reset) @@ -110,17 +123,37 @@ class LinkTrainingFSM( new MBInitFSM( linkTrainingParams, afeParams, + maxPatternCount = linkTrainingParams.maxPatternCount, ), ) mbInit.reset := ((nextState === LinkTrainingState.mbInit) && (currentState =/= LinkTrainingState.mbInit)) || reset.asBool + /** initialize MBInit IOs */ + mbInit.io.sbTrainIO.msgReq.nodeq() + mbInit.io.sbTrainIO.msgReqStatus.noenq() + mbInit.io.patternGeneratorIO.transmitReq.nodeq() + mbInit.io.patternGeneratorIO.resp.noenq() + + private val mbTrainer = Module( + new MBTrainer( + linkTrainingParams = linkTrainingParams, + afeParams = afeParams, + maxPatternCount = linkTrainingParams.maxPatternCount, + ), + ) + mbTrainer.reset := ((nextState === LinkTrainingState.mbTrain) && (currentState =/= LinkTrainingState.mbTrain)) || reset.asBool + mbTrainer.io.sbTrainIO.msgReq.nodeq() + mbTrainer.io.sbTrainIO.msgReqStatus.noenq() + mbTrainer.io.patternGeneratorIO.transmitReq.nodeq() + mbTrainer.io.patternGeneratorIO.resp.noenq() + mbTrainer.io.trainingOperationIO <> io.trainingOperationIO + private val rdiBringup = Module(new RdiBringup) rdiBringup.io.rdiIO <> io.rdi.rdiBringupIO rdiBringup.io.sbTrainIO.msgReq.nodeq() rdiBringup.io.sbTrainIO.msgReqStatus.noenq() val plStateStatus = WireInit(rdiBringup.io.rdiIO.plStateStatus) - // TODO: incorporate lpstatereq currentState := PriorityMux( Seq( (rdiBringup.io.rdiIO.plStateStatus === PhyState.reset, nextState), @@ -138,12 +171,6 @@ class LinkTrainingFSM( ), ), ) - // currentState := Mux( - // plStateStatus === PhyState.reset, - // nextState, - /* Mux(plStateStatus === PhyState.linkError, LinkTrainingState.linkError, - * Mux(plStateStatus === )), */ - // ) io.sidebandFSMIO.rxMode := Mux( currentState === LinkTrainingState.sbInit && (sbInitSubState === SBInitSubState.SEND_CLOCK || @@ -155,12 +182,6 @@ class LinkTrainingFSM( ) io.sidebandFSMIO.txMode := io.sidebandFSMIO.rxMode - /** initialize MBInit IOs */ - mbInit.io.sbTrainIO.msgReq.nodeq() - mbInit.io.sbTrainIO.msgReqStatus.noenq() - mbInit.io.patternGeneratorIO.transmitReq.nodeq() - mbInit.io.patternGeneratorIO.transmitPatternStatus.noenq() - /** TODO: should these ever be false? */ io.sidebandFSMIO.rxEn := true.B io.mainbandFSMIO.rxEn := (currentState =/= LinkTrainingState.reset) @@ -186,6 +207,12 @@ class LinkTrainingFSM( activeSubState := ActiveSubState.IDLE } + val pllLockTrigger = io.trainingOperationIO.pllLockTrigger.read + io.trainingOperationIO.pllLockTrigger.write.noenq() + when(pllLockTrigger) { + io.trainingOperationIO.pllLockTrigger.write.enq(false.B) + } + switch(currentState) { is(LinkTrainingState.reset) { io.mainbandFSMIO.rxEn := false.B @@ -194,30 +221,9 @@ class LinkTrainingFSM( (1 until linkTrainingParams.pllWaitTime), reset = resetFreqCtrValue, ) - switch(resetSubState) { - is(ResetSubState.INIT) { - when(io.mainbandFSMIO.pllLock && io.sidebandFSMIO.pllLock) { - io.mainbandFSMIO.txFreqSel := SpeedMode.speed4 - resetSubState := ResetSubState.FREQ_SEL_CYC_WAIT - resetFreqCtrValue := true.B - } - } - is(ResetSubState.FREQ_SEL_CYC_WAIT) { - when(freqSelCtrValue === (linkTrainingParams.pllWaitTime - 1).U) { - resetSubState := ResetSubState.FREQ_SEL_LOCK_WAIT - } - } - is(ResetSubState.FREQ_SEL_LOCK_WAIT) { - when( - io.mainbandFSMIO.pllLock && io.sidebandFSMIO.pllLock, - /** TODO: what is "Local SoC/Firmware not keeping the Physical Layer - * in RESET" - */ - ) { - nextState := LinkTrainingState.sbInit - } - } - + io.mainbandFSMIO.txFreqSel := SpeedMode.speed4 + when (if (afeParams.STANDALONE) { true.B } else { pllLockTrigger }) { + nextState := LinkTrainingState.sbInit } } is(LinkTrainingState.sbInit) { @@ -229,14 +235,17 @@ class LinkTrainingFSM( switch(sbInitSubState) { is(SBInitSubState.SEND_CLOCK) { - patternGenerator.io.patternGeneratorIO.transmitReq.bits.pattern := TransmitPattern.CLOCK_64_LOW_32 - patternGenerator.io.patternGeneratorIO.transmitReq.bits.sideband := true.B + patternGenerator.io.patternGeneratorIO.transmitReq.bits.pattern := TransmitPattern.CLOCK /** Timeout occurs after 8ms */ patternGenerator.io.patternGeneratorIO.transmitReq.bits.timeoutCycles := ( 0.008 * sbClockFreq, ).toInt.U + /** TODO: can't make these different lengths for some reason */ + patternGenerator.io.patternGeneratorIO.transmitReq.bits.patternCountMax := (128 + 64 * 4).U + patternGenerator.io.patternGeneratorIO.transmitReq.bits.patternDetectedCountMax := (128 + 64 * 4).U + patternGenerator.io.patternGeneratorIO.transmitReq.valid := true.B msgSource := MsgSource.PATTERN_GENERATOR when(patternGenerator.io.patternGeneratorIO.transmitReq.fire) { @@ -244,13 +253,13 @@ class LinkTrainingFSM( } } is(SBInitSubState.WAIT_CLOCK) { - patternGenerator.io.patternGeneratorIO.transmitPatternStatus.ready := true.B + patternGenerator.io.patternGeneratorIO.resp.ready := true.B msgSource := MsgSource.PATTERN_GENERATOR when( - patternGenerator.io.patternGeneratorIO.transmitPatternStatus.fire, + patternGenerator.io.patternGeneratorIO.resp.fire, ) { switch( - patternGenerator.io.patternGeneratorIO.transmitPatternStatus.bits, + patternGenerator.io.patternGeneratorIO.resp.bits.status, ) { is(MessageRequestStatusType.SUCCESS) { sbInitSubState := SBInitSubState.SB_OUT_OF_RESET_EXCH @@ -262,21 +271,11 @@ class LinkTrainingFSM( } } is(SBInitSubState.SB_OUT_OF_RESET_EXCH) { - sbMsgWrapper.io.trainIO.msgReq.bits.msg := SBMessage_factory( - SBM.SBINIT_OUT_OF_RESET, - "PHY", - true, - "PHY", - ) - /* sbMsgWrapper.io.trainIO.msgReq.bits.reqType := - * MessageRequestType.MSG_EXCH */ - // sbMsgWrapper.io.trainIO.msgReq.bits.msgTypeHasData := false.B - sbMsgWrapper.io.trainIO.msgReq.valid := true.B + val bitPat = SBM.SBINIT_OUT_OF_RESET + val reqType = MessageRequestType.EXCHANGE + val timeout = (0.008 * sbClockFreq).toInt + sendSidebandReq(bitPat, reqType, true, timeout) - sbMsgWrapper.io.trainIO.msgReq.bits.timeoutCycles := ( - 0.008 * sbClockFreq, - ).toInt.U - msgSource := MsgSource.SB_MSG_WRAPPER when(sbMsgWrapper.io.trainIO.msgReq.fire) { sbInitSubState := SBInitSubState.SB_OUT_OF_RESET_WAIT } @@ -296,20 +295,12 @@ class LinkTrainingFSM( } } is(SBInitSubState.SB_DONE_REQ) { - sbMsgWrapper.io.trainIO.msgReq.bits.msg := SBMessage_factory( + sendSidebandReq( SBM.SBINIT_DONE_REQ, - "PHY", - true, - "PHY", + MessageRequestType.EXCHANGE, + false, + (0.008 * sbClockFreq).toInt, ) - /* sbMsgWrapper.io.trainIO.msgReq.bits.reqType := - * MessageRequestType.MSG_REQ */ - // sbMsgWrapper.io.trainIO.msgReq.bits.msgTypeHasData := false.B - sbMsgWrapper.io.trainIO.msgReq.valid := true.B - sbMsgWrapper.io.trainIO.msgReq.bits.timeoutCycles := ( - 0.008 * sbClockFreq, - ).toInt.U - msgSource := MsgSource.SB_MSG_WRAPPER when(sbMsgWrapper.io.trainIO.msgReq.fire) { sbInitSubState := SBInitSubState.SB_DONE_REQ_WAIT } @@ -329,20 +320,12 @@ class LinkTrainingFSM( } } is(SBInitSubState.SB_DONE_RESP) { - sbMsgWrapper.io.trainIO.msgReq.bits.msg := SBMessage_factory( + sendSidebandReq( SBM.SBINIT_DONE_RESP, - "PHY", - remote = true, - "PHY", + MessageRequestType.EXCHANGE, + false, + (0.008 * sbClockFreq).toInt, ) - /* sbMsgWrapper.io.trainIO.msgReq.bits.reqType := - * MessageRequestType.MSG_RESP */ - sbMsgWrapper.io.trainIO.msgReq.valid := true.B - // sbMsgWrapper.io.trainIO.msgReq.bits.msgTypeHasData := false.B - msgSource := MsgSource.SB_MSG_WRAPPER - sbMsgWrapper.io.trainIO.msgReq.bits.timeoutCycles := ( - 0.008 * sbClockFreq, - ).toInt.U when(sbMsgWrapper.io.trainIO.msgReq.fire) { sbInitSubState := SBInitSubState.SB_DONE_RESP_WAIT } @@ -365,17 +348,33 @@ class LinkTrainingFSM( } is(LinkTrainingState.mbInit) { - /** TODO: can't use two message sources at the same time */ mbInit.io.sbTrainIO <> sbMsgWrapper.io.trainIO mbInit.io.patternGeneratorIO <> patternGenerator.io.patternGeneratorIO msgSource := MsgSource.SB_MSG_WRAPPER - when(mbInit.io.transition.asBool) { + when(mbInit.io.transition) { nextState := Mux( mbInit.io.error, LinkTrainingState.linkError, + // TODO: Transition to mbTrain in non-STANDALONE mode + // without getting stuck. + LinkTrainingState.linkInit, + ) + } + } + is(LinkTrainingState.mbTrain) { + + mbTrainer.io.sbTrainIO <> sbMsgWrapper.io.trainIO + sbMsgWrapper.reset := mbTrainer.io.sbMsgWrapperReset + mbTrainer.io.patternGeneratorIO <> patternGenerator.io.patternGeneratorIO + msgSource := MsgSource.SB_MSG_WRAPPER + when(mbTrainer.io.complete) { + nextState := Mux( + mbTrainer.io.err, + LinkTrainingState.linkError, LinkTrainingState.linkInit, ) } + } is(LinkTrainingState.linkInit) { rdiBringup.io.sbTrainIO <> sbMsgWrapper.io.trainIO @@ -397,4 +396,22 @@ class LinkTrainingFSM( } } + private def sendSidebandReq( + bitPat: BitPat, + reqType: MessageRequestType.Type, + repeat: Boolean, + timeout: Int, + ): Unit = { + sbMsgWrapper.io.trainIO.msgReq.bits.msg := SBMessage_factory( + bitPat, + "PHY", + true, + "PHY", + ) + sbMsgWrapper.io.trainIO.msgReq.bits.reqType := reqType + sbMsgWrapper.io.trainIO.msgReq.valid := true.B + sbMsgWrapper.io.trainIO.msgReq.bits.timeoutCycles := timeout.U + sbMsgWrapper.io.trainIO.msgReq.bits.repeat := repeat.B + msgSource := MsgSource.SB_MSG_WRAPPER + } } diff --git a/src/main/scala/logphy/LogPhyTypes.scala b/src/main/scala/logphy/LogPhyTypes.scala index 0cc764c1..d18bf46c 100644 --- a/src/main/scala/logphy/LogPhyTypes.scala +++ b/src/main/scala/logphy/LogPhyTypes.scala @@ -3,11 +3,13 @@ package logphy import chisel3._ import chisel3.util._ +import freechips.rocketchip.regmapper.{RegField, RegFieldDesc, RegReadFn, RegWriteFn} import sideband.SidebandParams import interfaces._ object LinkTrainingState extends ChiselEnum { - val reset, sbInit, mbInit, linkInit, active, linkError, retrain = Value + val reset, sbInit, mbInit, mbTrain, linkInit, active, linkError, retrain = + Value } object MsgSource extends ChiselEnum { @@ -28,10 +30,15 @@ class SBReqMsg extends Bundle { val msg = UInt(128.W) } +object MessageRequestType extends ChiselEnum { + val EXCHANGE, RECEIVE, SEND = Value +} + class MessageRequest extends Bundle { val msg = UInt(128.W) val timeoutCycles = UInt(64.W) - // val msgTypeHasData = Bool() + val reqType = MessageRequestType() + val repeat = Bool() } class MessageRequestStatus extends Bundle { @@ -47,7 +54,10 @@ object ClockModeParam extends ChiselEnum { } object TransmitPattern extends ChiselEnum { - val CLOCK_64_LOW_32 = Value(0.U) + val LFSR = Value(0.U) + val PER_LANE_ID = Value(1.U) + val VALTRAIN = Value(2.U) + val CLOCK = Value(3.U) } class SBIO(params: AfeParams) extends Bundle { @@ -68,12 +78,10 @@ class SBIO(params: AfeParams) extends Bundle { val rxData = Flipped(Decoupled(Bits(params.sbSerializerRatio.W))) } -class MainbandIO( +class MainbandLaneIO( afeParams: AfeParams, ) extends Bundle { - val fifoParams = Input(new FifoParams()) - /** Data to transmit on the mainband. * * Output from the async FIFO. @@ -95,7 +103,25 @@ class MainbandIO( ) } -class MainbandLaneIO( +class MainbandLaneDataValid(afeParams: AfeParams) extends Bundle { + val data = Vec(afeParams.mbLanes, Bits(afeParams.mbSerializerRatio.W)) + val valid = Vec(afeParams.mbSerializerRatio, Bool()) +} + +class MainbandLaneIOWithValid(afeParams: AfeParams) extends Bundle { + val tx = Decoupled(new MainbandLaneDataValid(afeParams)) + val rx = Flipped(Decoupled(new MainbandLaneDataValid(afeParams))) +} + +class MainbandLaneIOWithFifoIO( + afeParams: AfeParams, +) extends MainbandLaneIO(afeParams) { + + val fifoParams = Input(new FifoParams()) + +} + +class MainbandIO( afeParams: AfeParams, ) extends Bundle { @@ -106,7 +132,7 @@ class MainbandLaneIO( ) val rxData = - Valid(Bits((afeParams.mbLanes * afeParams.mbSerializerRatio).W)) + Decoupled(Bits((afeParams.mbLanes * afeParams.mbSerializerRatio).W)) } class SidebandLaneIO( @@ -122,3 +148,37 @@ class SidebandLaneIO( val rxData = Decoupled(Bits(sbParams.sbNodeMsgWidth.W)) } + +class RegisterRWIO[T <: Data](gen: T) extends Bundle { + val write = Flipped(Decoupled(gen)) + val read = Output(gen) + + def regWrite: RegWriteFn = RegWriteFn((valid, data) => { + write.valid := valid + write.bits := data.asTypeOf(gen) + write.ready + }) + + def regRead: RegReadFn = RegReadFn(read.asUInt) + + def getDataWidth = gen.getWidth + + def regField(desc: RegFieldDesc): RegField = { + RegField(gen.getWidth, regRead, regWrite, desc) + } +} + +class RegisterRW[T <: Data](val init: T, name: String) { + val reg: T = RegInit(init).suggestName(name) + + def io = new RegisterRWIO(chiselTypeOf(init)) + + def connect(io: RegisterRWIO[T]): Unit = { + io.write.deq() + when(io.write.fire) { + reg := io.write.bits + } + + io.read := reg + } +} diff --git a/src/main/scala/logphy/LogicalPhy.scala b/src/main/scala/logphy/LogicalPhy.scala index 75f34099..3bb11fc7 100644 --- a/src/main/scala/logphy/LogicalPhy.scala +++ b/src/main/scala/logphy/LogicalPhy.scala @@ -4,10 +4,11 @@ package logphy import interfaces._ import sideband._ import chisel3._ +import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor import freechips.rocketchip.util.AsyncQueueParams class LogicalPhy( - myId: BigInt, linkTrainingParams: LinkTrainingParams, afeParams: AfeParams, rdiParams: RdiParams, @@ -18,8 +19,18 @@ class LogicalPhy( val io = IO(new Bundle { val rdi = Flipped(new Rdi(rdiParams)) - val mbAfe = new MainbandAfeIo(afeParams) + val mbAfe = + if (afeParams.STANDALONE) Some(new MainbandAfeIo(afeParams)) else None + val phyAfe = + if (afeParams.STANDALONE) None + else Some(new MainbandLaneIOWithValid(afeParams)) val sbAfe = new SidebandAfeIo(afeParams) + val train = + if (afeParams.STANDALONE) None + else + Some( + new TrainingOperation(afeParams, linkTrainingParams.maxPatternCount), + ) }) val trainingModule = { @@ -28,9 +39,16 @@ class LogicalPhy( ) } - trainingModule.io.mainbandFSMIO.pllLock <> io.mbAfe.pllLock - trainingModule.io.sidebandFSMIO.pllLock <> io.sbAfe.pllLock - trainingModule.io.mainbandFSMIO.rxEn <> io.mbAfe.rxEn + /** TODO: replace this with MMIO module instantiations */ + if (afeParams.STANDALONE) { + trainingModule.io.trainingOperationIO := DontCare + } else { + trainingModule.io.trainingOperationIO <> io.train.get + } + + if (afeParams.STANDALONE) { + trainingModule.io.mainbandFSMIO.rxEn <> io.mbAfe.get.rxEn + } trainingModule.io.sidebandFSMIO.rxEn <> io.sbAfe.rxEn trainingModule.io.rdi.rdiBringupIO.lpStateReq <> io.rdi.lpStateReq @@ -51,7 +69,9 @@ class LogicalPhy( io.rdi.plPhyInRecenter := io.rdi.plStateStatus === PhyState.retrain io.rdi.plSpeedMode <> trainingModule.io.mainbandFSMIO.txFreqSel - io.mbAfe.txFreqSel <> trainingModule.io.mainbandFSMIO.txFreqSel + if (afeParams.STANDALONE) { + io.mbAfe.get.txFreqSel <> trainingModule.io.mainbandFSMIO.txFreqSel + } io.rdi.plLinkWidth := PhyWidth.width16 io.rdi.plClkReq <> trainingModule.io.rdi.rdiBringupIO.plClkReq io.rdi.plWakeAck <> trainingModule.io.rdi.rdiBringupIO.plWakeAck @@ -63,30 +83,55 @@ class LogicalPhy( io.rdi.lpLinkError <> trainingModule.io.rdi.rdiBringupIO.lpLinkError /** TODO: is this correct behavior, look at spec */ - io.rdi.plInbandPres := trainingModule.io.currentState === LinkTrainingState.active + io.rdi.plInbandPres := trainingModule.io.currentState === LinkTrainingState.linkInit || trainingModule.io.currentState === LinkTrainingState.active val rdiDataMapper = Module(new RdiDataMapper(rdiParams, afeParams)) - val lanes = Module(new Lanes(afeParams, laneAsyncQueueParams)) + val lanes = + if (afeParams.STANDALONE) { + Module(new Lanes(afeParams, laneAsyncQueueParams)) + } else { + Module(new LanesNoFifo(afeParams)) + } + + lanes.io.scramble := trainingModule.io.currentState === LinkTrainingState.active + when(trainingModule.io.currentState === LinkTrainingState.active) { + rdiDataMapper.io.mainbandIO <> lanes.io.mainbandIO + trainingModule.io.mainbandFSMIO.mainbandIO.rxData.noenq() + trainingModule.io.mainbandFSMIO.mainbandIO.txData.nodeq() + }.otherwise { + rdiDataMapper.io.mainbandIO.rxData.noenq() + rdiDataMapper.io.mainbandIO.txData.nodeq() + trainingModule.io.mainbandFSMIO.mainbandIO <> lanes.io.mainbandIO + } /** Connect internal FIFO to AFE */ - lanes.io.mainbandIo.txData <> io.mbAfe.txData - lanes.io.mainbandIo.rxData <> io.mbAfe.rxData - lanes.io.mainbandIo.fifoParams <> io.mbAfe.fifoParams - rdiDataMapper.io.mainbandLaneIO <> lanes.io.mainbandLaneIO + if (afeParams.STANDALONE) { + lanes.io.mainbandLaneIO.txData <> io.mbAfe.get.txData + lanes.io.mainbandLaneIO.rxData <> io.mbAfe.get.rxData + lanes.asInstanceOf[Lanes].asyncQueueIO <> io.mbAfe.get.fifoParams + } else { + + io.phyAfe.get.tx <> lanes.io.mainbandLaneIO.txData.map(f => { + val x = Wire(chiselTypeOf(io.phyAfe.get.tx.bits)) + x.data := f + x.valid := VecInit((0 until afeParams.mbSerializerRatio/8).flatMap(_ => Seq.fill(4)(true.B) ++ Seq.fill(4)(false.B))) + x + }) + io.phyAfe.get.rx.map(_.data) <> lanes.io.mainbandLaneIO.rxData + } /** Connect RDI to Mainband IO */ rdiDataMapper.io.rdi.lpData <> io.rdi.lpData io.rdi.plData <> rdiDataMapper.io.rdi.plData private val sidebandChannel = - Module(new PHYSidebandChannel(myId, sbParams, fdiParams)) + Module(new PHYSidebandChannel(sbParams = sbParams, fdiParams = fdiParams)) assert( afeParams.sbSerializerRatio == 1, "connecting sideband module directly to training module, sb serializer ratio must be 1!", ) - /** TODO: Double check that this is the right direction */ sidebandChannel.io.to_upper_layer.tx.bits <> io.rdi.plConfig.bits sidebandChannel.io.to_upper_layer.tx.valid <> io.rdi.plConfig.valid sidebandChannel.io.to_upper_layer.tx.credit <> io.rdi.plConfigCredit @@ -101,9 +146,7 @@ class LogicalPhy( sidebandChannel.io.inner.inputMode := trainingModule.io.sidebandFSMIO.txMode sidebandChannel.io.inner.rxMode := trainingModule.io.sidebandFSMIO.rxMode - /** TODO: layer to node above not connected? Not sure when might receive SB - * packet from above layer - */ + /** Currently no situation where would receive SB packet from above layer. */ sidebandChannel.io.inner.switcherBundle.layer_to_node_above.noenq() sidebandChannel.io.inner.switcherBundle.node_to_layer_above.nodeq() diff --git a/src/main/scala/logphy/MBInitFSM.scala b/src/main/scala/logphy/MBInitFSM.scala index 1b88d619..221080d9 100644 --- a/src/main/scala/logphy/MBInitFSM.scala +++ b/src/main/scala/logphy/MBInitFSM.scala @@ -18,11 +18,13 @@ case class MBTrainingParams( class MBInitFSM( linkTrainingParams: LinkTrainingParams, afeParams: AfeParams, + maxPatternCount: Int, ) extends Module { val io = IO(new Bundle { val sbTrainIO = Flipped(new SBMsgWrapperTrainIO) - val patternGeneratorIO = Flipped(new PatternGeneratorIO) + val patternGeneratorIO = + Flipped(new PatternGeneratorIO(afeParams, maxPatternCount)) val transition = Output(Bool()) val error = Output(Bool()) }) @@ -54,7 +56,7 @@ class MBInitFSM( io.sbTrainIO.msgReq.noenq() io.sbTrainIO.msgReqStatus.nodeq() io.patternGeneratorIO.transmitReq.noenq() - io.patternGeneratorIO.transmitPatternStatus.nodeq() + io.patternGeneratorIO.resp.nodeq() /** Initialize params */ private val voltageSwing = RegInit( @@ -109,15 +111,14 @@ class MBInitFSM( if (req) SBM.MBINIT_PARAM_CONFIG_REQ else SBM.MBINIT_PARAM_CONFIG_RESP, "PHY", - false, + remote = true, "PHY", data, ) + msgReq.repeat := false.B - // msgReq.msgTypeHasData := true.B msgReq.timeoutCycles := (0.008 * sbClockFreq).toInt.U - // msgReq.reqType := (if (req) MessageRequestType.MSG_REQ - // else MessageRequestType.MSG_RESP) + msgReq.reqType := MessageRequestType.EXCHANGE msgReq } diff --git a/src/main/scala/logphy/MBTrainer.scala b/src/main/scala/logphy/MBTrainer.scala new file mode 100644 index 00000000..e1d6ce16 --- /dev/null +++ b/src/main/scala/logphy/MBTrainer.scala @@ -0,0 +1,399 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import interfaces._ +import chisel3._ +import chisel3.util._ +import sideband.{SBM, SBMessage_factory} + +/** TODO: make timeout cycles optional */ + +class TrainingOperation(afeParams: AfeParams, maxPatternCount: Int) + extends Bundle { + val maxPatternCountWidth = log2Ceil(maxPatternCount + 1) + val pattern = Input(TransmitPattern()) + val patternUICount = Input(UInt(maxPatternCountWidth.W)) + val outputValid = Output(Bool()) + val errorCounts = Output(Vec(afeParams.mbLanes, UInt(maxPatternCountWidth.W))) + val pllLockTrigger = Flipped(new RegisterRWIO(Bool())) + val triggerNew = Flipped(new RegisterRWIO(Bool())) + val triggerExit = Flipped(new RegisterRWIO(Bool())) +} +class MBTrainer( + linkTrainingParams: LinkTrainingParams, + afeParams: AfeParams, + maxPatternCount: Int, +) extends Module { + + val sbClockFreq = + linkTrainingParams.sbClockFreqAnalog / afeParams.sbSerializerRatio + + val io = IO(new Bundle { + val trainingOperationIO = new TrainingOperation(afeParams, maxPatternCount) + val sbTrainIO = Flipped(new SBMsgWrapperTrainIO) + val sbMsgWrapperReset = Output(Bool()) + val patternGeneratorIO = + Flipped(new PatternGeneratorIO(afeParams, maxPatternCount)) + val complete = Output(Bool()) + val err = Output(Bool()) + }) + + io.sbTrainIO.msgReq.noenq() + io.sbTrainIO.msgReqStatus.nodeq() + io.sbTrainIO.msgReq.bits.repeat := false.B + io.patternGeneratorIO.transmitReq.noenq() + io.patternGeneratorIO.resp.nodeq() + + private object State extends ChiselEnum { + val WAIT_PTTEST_REQ_SEND, WAIT_PTTEST_REQ, SEND_PTTEST_REQ, + SEND_PTTEST_REQ_WAIT, SEND_PTTEST_RESP, SEND_PTTEST_RESP_WAIT, + TRAIN_SEND_PATTERN_REQ, TRAIN_WAIT_PATTERN_RESP, + TRAIN_PATTERN_FINISHED_SEND, TRAIN_PATTERN_FINISHED_WAIT, + PTTEST_RESULTS_REQ_SEND, PTTEST_RESULTS_REQ_WAIT, + PTTEST_RESULTS_RESP_SEND, PTTEST_RESULTS_RESP_WAIT, + PTTEST_END_TEST_REQ_SEND, PTTEST_END_TEST_REQ_WAIT, + PTTEST_END_TEST_RESP_SEND, PTTEST_END_TEST_RESP_WAIT, COMPLETE, ERR = + Value + } + private val currentState = RegInit(State.WAIT_PTTEST_REQ_SEND) + + io.complete := currentState === State.COMPLETE || currentState === State.ERR + io.err := currentState === State.ERR + + private val txDtoCPointReq = RegInit(0.U.asTypeOf(new TxDtoCPointReq)) + + io.sbMsgWrapperReset := false.B + val triggerNew = io.trainingOperationIO.triggerNew.read + val triggerExit = io.trainingOperationIO.triggerExit.read + val pllLockTrigger = io.trainingOperationIO.pllLockTrigger.read + + io.trainingOperationIO.triggerExit.write.noenq() + io.trainingOperationIO.triggerNew.write.noenq() + io.trainingOperationIO.pllLockTrigger.write.noenq() + when(triggerNew) { + io.trainingOperationIO.triggerNew.write.enq(false.B) + } + when(triggerExit) { + io.trainingOperationIO.triggerExit.write.enq(false.B) + } + when(pllLockTrigger) { + io.trainingOperationIO.pllLockTrigger.write.enq(false.B) + } + + when(triggerNew) { + currentState := State.SEND_PTTEST_REQ + io.sbMsgWrapperReset := true.B + + val pointReq = Wire(new TxDtoCPointReq) + pointReq := DontCare + pointReq.dataPattern := io.trainingOperationIO.pattern.asUInt + pointReq.iterationCount := io.trainingOperationIO.patternUICount + txDtoCPointReq := pointReq + }.elsewhen(triggerExit) { + currentState := State.PTTEST_END_TEST_REQ_SEND + io.sbMsgWrapperReset := true.B + } + + private class TxDtoCPointReq extends Bundle { + val reserved = UInt(4.W) + val comparisonMode = UInt(1.W) + val iterationCount = UInt(16.W) + val idleCount = UInt(16.W) + val burstCount = UInt(16.W) + val patternMode = UInt(1.W) + val clockPhaseControl = UInt(4.W) + val validPattern = UInt(3.W) + val dataPattern = UInt(3.W) + } + + private class TxDtoCResultsResp extends Bundle { + val msgInfo = new Bundle { + val reserved = UInt(10.W) + val validLaneComparisonResults = UInt(1.W) + val cumulativeResults = UInt(1.W) + val redundantLaneComparison = UInt(4.W) + } + val data = new Bundle { + val reserved = UInt(48.W) + val lanePassResults = Vec(afeParams.mbLanes, Bool()) + } + } + + def formStartTxDtoCPointReq( + maxErrors: UInt, + req: TxDtoCPointReq, + reqType: MessageRequestType.Type, + ): MessageRequest = { + val data = Wire(UInt(64.W)) + val msgReq = Wire(new MessageRequest) + data := req.asTypeOf(UInt(64.W)) + msgReq.msg := SBMessage_factory( + SBM.MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_REQ, + src = "PHY", + remote = true, + dst = "PHY", + data, + msgInfo = maxErrors(15, 0), + ) + msgReq.timeoutCycles := (0.008 * sbClockFreq).toInt.U + msgReq.reqType := reqType + msgReq.repeat := false.B + msgReq + } + + def formStartTxDtoCPointResp( + reqType: MessageRequestType.Type, + ): MessageRequest = { + val msgReq = Wire(new MessageRequest) + msgReq.msg := SBMessage_factory( + SBM.MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_RESP, + src = "PHY", + remote = true, + dst = "PHY", + ) + msgReq.timeoutCycles := (0.008 * sbClockFreq).toInt.U + msgReq.reqType := reqType + msgReq.repeat := false.B + msgReq + } + + private val errorCount = Reg( + chiselTypeOf(io.patternGeneratorIO.resp.bits.errorCount), + ) + private val errorCountValid = RegInit(false.B) + io.trainingOperationIO.errorCounts := errorCount + io.trainingOperationIO.outputValid := errorCountValid + + switch(currentState) { + is(State.WAIT_PTTEST_REQ_SEND) { + + /** Send the request to the SB trainer to wait for the Pt Test Req msg */ + // io.sbTrainIO.msgReq.valid := true.B + val txDtoCPointReq = Wire(new TxDtoCPointReq) + txDtoCPointReq := DontCare + io.sbTrainIO.msgReq.enq( + formStartTxDtoCPointReq( + 0.U, + txDtoCPointReq, + MessageRequestType.RECEIVE, + ), + ) + when(io.sbTrainIO.msgReq.fire) { + currentState := State.WAIT_PTTEST_REQ + } + } + is(State.WAIT_PTTEST_REQ) { + + /** Wait for SB trainer to indicate that Pt Test Req message was received + */ + io.sbTrainIO.msgReqStatus.ready := true.B + when(io.sbTrainIO.msgReqStatus.fire) { + txDtoCPointReq := io.sbTrainIO.msgReqStatus.bits.data + .asTypeOf(new TxDtoCPointReq) + + when( + io.sbTrainIO.msgReqStatus.bits.status === MessageRequestStatusType.ERR, + ) { + assert( + false.B, + "SB Message wrapper should not throw error in MB trainer", + ) + }.otherwise { + currentState := State.SEND_PTTEST_RESP + } + } + } + is(State.SEND_PTTEST_REQ) { + + /** Send Pt Test Req message (skip if already received Pt Test Req) */ + io.sbTrainIO.msgReq.valid := true.B + io.sbTrainIO.msgReq.bits := formStartTxDtoCPointReq( + 0.U, + txDtoCPointReq, + MessageRequestType.SEND, + ) + when(io.sbTrainIO.msgReq.fire) { + currentState := State.SEND_PTTEST_REQ_WAIT + } + } + is(State.SEND_PTTEST_REQ_WAIT) { + + /** Wait for Pt Test Request to be received (skip if already received pt + * test request) + */ + receiveSBMsg(State.SEND_PTTEST_RESP) + } + is(State.SEND_PTTEST_RESP) { + + /** Have SB Trainer send a response */ + sendSBReq( + SBM.MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_RESP, + MessageRequestType.EXCHANGE, + State.SEND_PTTEST_RESP_WAIT, + ) + } + is(State.SEND_PTTEST_RESP_WAIT) { + + /** Wait for SB trainer to indicate Start Pt Test response was a success + */ + receiveSBMsg(State.TRAIN_SEND_PATTERN_REQ) + } + is(State.TRAIN_SEND_PATTERN_REQ) { + + /** Request pattern generator to complete the pattern test */ + io.patternGeneratorIO.transmitReq.bits.pattern := txDtoCPointReq.dataPattern + .asUInt(1, 0) + .asTypeOf(TransmitPattern()) + io.patternGeneratorIO.transmitReq.bits.timeoutCycles := (0.008 * sbClockFreq).toInt.U + io.patternGeneratorIO.transmitReq.bits.patternCountMax := txDtoCPointReq.iterationCount + io.patternGeneratorIO.transmitReq.bits.patternDetectedCountMax := txDtoCPointReq.iterationCount + io.patternGeneratorIO.transmitReq.valid := true.B + when(io.patternGeneratorIO.transmitReq.fire) { + currentState := State.TRAIN_WAIT_PATTERN_RESP + } + } + is(State.TRAIN_WAIT_PATTERN_RESP) { + + /** Wait for pattern generator to indicate that test is complete */ + io.patternGeneratorIO.resp.ready := true.B + when(io.patternGeneratorIO.resp.fire) { + when( + io.patternGeneratorIO.resp.bits.status === MessageRequestStatusType.ERR, + ) { + currentState := State.ERR + } + .otherwise { + errorCount := io.patternGeneratorIO.resp.bits.errorCount + currentState := State.PTTEST_RESULTS_REQ_SEND + } + } + } + is(State.PTTEST_RESULTS_REQ_SEND) { + + /** Request SB trainer to send results request message */ + io.sbTrainIO.msgReq.valid := true.B + io.sbTrainIO.msgReq.bits.msg := SBMessage_factory( + SBM.MBTRAIN_TX_INIT_D_TO_C_RESULTS_REQ, + "PHY", + true, + "PHY", + ) + io.sbTrainIO.msgReq.bits.reqType := MessageRequestType.EXCHANGE + io.sbTrainIO.msgReq.bits.timeoutCycles := (0.008 * sbClockFreq).toInt.U + when(io.sbTrainIO.msgReq.fire) { + + /** Received result req, no need to send result req */ + currentState := State.PTTEST_RESULTS_REQ_WAIT + } + } + is(State.PTTEST_RESULTS_REQ_WAIT) { + + /** Wait for SB trainer to indicate that results was a success */ + receiveSBMsg(State.PTTEST_RESULTS_RESP_SEND) + } + is(State.PTTEST_RESULTS_RESP_SEND) { + + /** Request SB messenger to exchange results resp */ + val resultsResp = Wire(new TxDtoCResultsResp) + resultsResp := DontCare + io.sbTrainIO.msgReq.bits.msg := SBMessage_factory( + SBM.MBTRAIN_TX_INIT_D_TO_C_RESULTS_RESP, + "PHY", + true, + "PHY", + resultsResp.data.asUInt, + resultsResp.msgInfo.asUInt, + ) + io.sbTrainIO.msgReq.bits.reqType := MessageRequestType.EXCHANGE + io.sbTrainIO.msgReq.bits.timeoutCycles := (0.008 * sbClockFreq).toInt.U + io.sbTrainIO.msgReq.valid := true.B + when(io.sbTrainIO.msgReq.fire) { + currentState := State.PTTEST_RESULTS_RESP_WAIT + } + } + is(State.PTTEST_RESULTS_RESP_WAIT) { + receiveSBMsg(State.TRAIN_PATTERN_FINISHED_SEND) + } + is(State.TRAIN_PATTERN_FINISHED_SEND) { + + /** From here, wait for a few things: (1) re-trigger training (2) finish + * training trigger (3) SB message indicating training is finished + */ + + io.sbTrainIO.msgReq.valid := true.B + errorCountValid := true.B + io.sbTrainIO.msgReq.bits.msg := SBMessage_factory( + SBM.MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_REQ, + "PHY", + true, + "PHY", + ) + io.sbTrainIO.msgReq.bits.reqType := MessageRequestType.RECEIVE + io.sbTrainIO.msgReq.bits.timeoutCycles := (0.008 * sbClockFreq).toInt.U + when(io.sbTrainIO.msgReq.fire) { + currentState := State.TRAIN_PATTERN_FINISHED_WAIT + } + } + is(State.TRAIN_PATTERN_FINISHED_WAIT) { + val nextState = State.PTTEST_END_TEST_RESP_SEND + receiveSBMsg(nextState) + } + is(State.PTTEST_END_TEST_REQ_SEND) { + val reqType = MessageRequestType.SEND + sendSBReq( + SBM.MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_REQ, + reqType, + State.PTTEST_END_TEST_REQ_WAIT, + ) + } + is(State.PTTEST_END_TEST_REQ_WAIT) { + receiveSBMsg(State.PTTEST_END_TEST_RESP_SEND) + } + is(State.PTTEST_END_TEST_RESP_SEND) { + sendSBReq( + SBM.MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_RESP, + MessageRequestType.EXCHANGE, + State.PTTEST_END_TEST_RESP_WAIT, + ) + } + is(State.PTTEST_END_TEST_RESP_WAIT) { + receiveSBMsg(State.COMPLETE) + } + is(State.COMPLETE) {} + is(State.ERR) {} + + } + + private def receiveSBMsg(nextState: State.Type) = { + io.sbTrainIO.msgReqStatus.ready := true.B + when(io.sbTrainIO.msgReqStatus.fire) { + when( + io.sbTrainIO.msgReqStatus.bits.status === MessageRequestStatusType.ERR, + ) { + currentState := State.ERR + }.otherwise { + currentState := nextState + } + } + } + + private def sendSBReq( + message: BitPat, + reqType: MessageRequestType.Type, + nextState: State.Type, + ): Unit = { + io.sbTrainIO.msgReq.bits.msg := SBMessage_factory( + message, + "PHY", + remote = true, + "PHY", + ) + io.sbTrainIO.msgReq.bits.reqType := reqType + io.sbTrainIO.msgReq.bits.timeoutCycles := (0.008 * sbClockFreq).toInt.U + io.sbTrainIO.msgReq.valid := true.B + when(io.sbTrainIO.msgReq.fire) { + currentState := nextState + } + } + +} diff --git a/src/main/scala/logphy/PatternGenerator.scala b/src/main/scala/logphy/PatternGenerator.scala index 858cac19..c42ad339 100644 --- a/src/main/scala/logphy/PatternGenerator.scala +++ b/src/main/scala/logphy/PatternGenerator.scala @@ -6,151 +6,112 @@ import chisel3.util._ import sideband.SidebandParams import interfaces._ -class PatternGeneratorIO extends Bundle { +class PatternGeneratorIO(afeParams: AfeParams, maxPatternCount: Int) + extends Bundle { + val maxPatternCountWidth = log2Ceil(maxPatternCount + 1) val transmitReq = Flipped(Decoupled(new Bundle { val pattern = TransmitPattern() val timeoutCycles = UInt(32.W) - val sideband = Bool() - })) // data to transmit & receive over SB - val transmitPatternStatus = Decoupled(MessageRequestStatusType()) + val patternCountMax = UInt(maxPatternCountWidth.W) + val patternDetectedCountMax = UInt(maxPatternCountWidth.W) + })) + val resp = Decoupled(new Bundle { + val status = MessageRequestStatusType() + val errorCount = Output( + Vec(afeParams.mbLanes, UInt(maxPatternCountWidth.W)), + ) + }) + } class PatternGenerator( afeParams: AfeParams, sbParams: SidebandParams, + maxPatternCount: Int, ) extends Module { + val maxPatternCountWidth = log2Ceil(maxPatternCount + 1) val io = IO(new Bundle { - val patternGeneratorIO = new PatternGeneratorIO() + val patternGeneratorIO = new PatternGeneratorIO(afeParams, maxPatternCount) - /** for now, assume want to transmit on sideband IO only */ - // val mainbandLaneIO = Flipped(new MainbandLaneIO(afeParams)) + val mainbandIO = Flipped(new MainbandIO(afeParams)) val sidebandLaneIO = Flipped(new SidebandLaneIO(sbParams)) }) - /** TODO: remove */ - // io.mainbandLaneIO.txData.noenq() + val patternWriter = Module( + new PatternWriter(sbParams, afeParams, maxPatternCount), + ) + val patternReader = Module( + new PatternReader(sbParams, afeParams, maxPatternCount), + ) + + patternWriter.io.sbTxData <> io.sidebandLaneIO.txData + patternWriter.io.mbTxData.map( + LanesToOne(_, afeParams.mbLanes, afeParams.mbSerializerRatio), + ) <> io.mainbandIO.txData + patternReader.io.sbRxData <> io.sidebandLaneIO.rxData + patternReader.io.mbRxData <> io.mainbandIO.rxData.map( + OneToLanes(_, afeParams.mbLanes, afeParams.mbSerializerRatio), + ) + + private val inProgress = WireInit( + patternWriter.io.resp.inProgress || patternReader.io.resp.inProgress, + ) - private val writeInProgress = RegInit(false.B) - private val readInProgress = RegInit(false.B) - private val inProgress = WireInit(writeInProgress || readInProgress) - private val pattern = RegInit(TransmitPattern.CLOCK_64_LOW_32) - private val sideband = RegInit(true.B) + private val inputsValid = RegInit(false.B) + private val pattern = RegInit(TransmitPattern.CLOCK) private val timeoutCycles = RegInit(0.U(32.W)) private val status = RegInit(MessageRequestStatusType.SUCCESS) + private val errorCount = RegInit( + VecInit(Seq.fill(afeParams.mbLanes)(0.U(maxPatternCount.W))), + ) private val statusValid = RegInit(false.B) + private val patternCountMax = RegInit(0.U(maxPatternCountWidth.W)) + private val patternDetectedCountMax = RegInit(0.U(maxPatternCountWidth.W)) + + patternWriter.io.request.bits.pattern := pattern + patternWriter.io.request.bits.patternCountMax := patternCountMax + patternReader.io.request.bits.pattern := pattern + patternReader.io.request.bits.patternCountMax := patternDetectedCountMax + patternWriter.io.request.valid := inputsValid + patternReader.io.request.valid := inputsValid + io.patternGeneratorIO.transmitReq.ready := (inProgress === false.B) - io.patternGeneratorIO.transmitPatternStatus.valid := statusValid - io.patternGeneratorIO.transmitPatternStatus.bits := status + io.patternGeneratorIO.resp.valid := statusValid + io.patternGeneratorIO.resp.bits.status := status + io.patternGeneratorIO.resp.bits.errorCount := errorCount when(io.patternGeneratorIO.transmitReq.fire) { - writeInProgress := true.B - readInProgress := true.B pattern := io.patternGeneratorIO.transmitReq.bits.pattern - sideband := io.patternGeneratorIO.transmitReq.bits.sideband timeoutCycles := io.patternGeneratorIO.transmitReq.bits.timeoutCycles + patternCountMax := io.patternGeneratorIO.transmitReq.bits.patternCountMax + patternDetectedCountMax := io.patternGeneratorIO.transmitReq.bits.patternDetectedCountMax + inputsValid := true.B statusValid := false.B } - /** clock gating is not implemented, so for now, just send 128 bits of regular - * clock data - */ - val clockPatternShiftReg = RegInit( - "h_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U, - ) - val patternToTransmit = WireInit(0.U(sbParams.sbNodeMsgWidth.W)) - val patternDetectedCount = RegInit(0.U(log2Ceil(128 * 2 + 1).W)) - val patternWrittenCount = RegInit(0.U(log2Ceil(2 + 1).W)) - - val patternWrittenCountMax = Seq( - TransmitPattern.CLOCK_64_LOW_32 -> 2.U, - ) - - val patternDetectedCountMax = Seq( - TransmitPattern.CLOCK_64_LOW_32 -> 128.U, - ) - - io.sidebandLaneIO.txData.valid := writeInProgress - io.sidebandLaneIO.txData.bits := patternToTransmit - io.sidebandLaneIO.rxData.ready := readInProgress - - when(io.patternGeneratorIO.transmitPatternStatus.fire) { + when(io.patternGeneratorIO.resp.fire) { statusValid := false.B } + /** handle timeouts and completion */ when(inProgress) { timeoutCycles := timeoutCycles - 1.U - when(timeoutCycles === 0.U) { - status := MessageRequestStatusType.ERR + val timeout = timeoutCycles === 0.U + val complete = + patternWriter.io.resp.complete && patternReader.io.resp.complete + + when(timeout || complete) { + status := Mux( + timeout, + MessageRequestStatusType.ERR, + MessageRequestStatusType.SUCCESS, + ) statusValid := true.B - writeInProgress := false.B - readInProgress := false.B - patternWrittenCount := 0.U - patternDetectedCount := 0.U - }.elsewhen( - (patternWrittenCount >= MuxLookup(pattern, 0.U)( - patternWrittenCountMax, - )) && (patternDetectedCount >= MuxLookup(pattern, 0.U)( - patternDetectedCountMax, - )), - ) { - statusValid := true.B - status := MessageRequestStatusType.SUCCESS - writeInProgress := false.B - readInProgress := false.B - patternWrittenCount := 0.U - patternDetectedCount := 0.U + patternWriter.reset := true.B + patternReader.reset := true.B + inputsValid := false.B + errorCount := patternReader.io.resp.errorCount } } - when(writeInProgress) { - switch(pattern) { - - /** Patterns may be different lengths, etc. so may be best to handle - * separately, for now - */ - is(TransmitPattern.CLOCK_64_LOW_32) { - patternToTransmit := clockPatternShiftReg( - sbParams.sbNodeMsgWidth - 1, - 0, - ) - when(io.sidebandLaneIO.txData.fire) { - /* clockPatternShiftReg := (clockPatternShiftReg >> - * sbParams.sbNodeMsgWidth.U).asUInt & */ - // (clockPatternShiftReg << - // (clockPatternShiftReg.getWidth.U - sbParams.sbNodeMsgWidth.U)) - printf("pattern written count: %d\n", patternWrittenCount) - patternWrittenCount := patternWrittenCount + 1.U - } - } - } - - } - - when(readInProgress) { - switch(pattern) { - - is(TransmitPattern.CLOCK_64_LOW_32) { - - assert( - sbParams.sbNodeMsgWidth == 128, - "comparing with 128 bit clock pattern", - ) - val patternToDetect = "h_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U( - sbParams.sbNodeMsgWidth.W, - ) - when(io.sidebandLaneIO.rxData.fire) { - - /** detect clock UI pattern -- as long as the pattern is correctly - * aligned, this is simple - */ - when(io.sidebandLaneIO.rxData.bits === patternToDetect) { - patternDetectedCount := patternDetectedCount + sbParams.sbNodeMsgWidth.U - } - } - - } - } - - } - } diff --git a/src/main/scala/logphy/PatternReader.scala b/src/main/scala/logphy/PatternReader.scala new file mode 100644 index 00000000..1b4973ff --- /dev/null +++ b/src/main/scala/logphy/PatternReader.scala @@ -0,0 +1,88 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chisel3.util._ +import interfaces.AfeParams +import sideband.SidebandParams + +class PatternReader( + sbParams: SidebandParams, + afeParams: AfeParams, + maxPatternCount: Int, +) extends Module { + private val maxPatternWidth = log2Ceil(maxPatternCount + 1) + val io = IO(new Bundle { + val request = Flipped(Valid(new Bundle { + val pattern = TransmitPattern() + val patternCountMax = UInt(maxPatternWidth.W) + })) + val resp = new Bundle { + val complete = Output(Bool()) + val inProgress = Output(Bool()) + val errorCount = Output( + Vec(afeParams.mbLanes, UInt(maxPatternWidth.W)), + ) + } + val sbRxData = Flipped(Decoupled(Bits(sbParams.sbNodeMsgWidth.W))) + val mbRxData = Flipped( + Decoupled(Vec(afeParams.mbLanes, UInt(afeParams.mbSerializerRatio.W))), + ) + }) + + private val readInProgress = RegInit(false.B) + private val patternDetectedCount = RegInit(0.U(maxPatternWidth.W)) + io.resp.inProgress := readInProgress + io.resp.complete := patternDetectedCount >= io.request.bits.patternCountMax + + when(io.request.valid && !readInProgress) { + readInProgress := true.B + } + + /** increment error count */ + private val errorCount = RegInit( + VecInit(Seq.fill(afeParams.mbLanes)(0.U(maxPatternWidth.W))), + ) + io.resp.errorCount := errorCount + private val errorCounter = Module( + new ErrorCounter(afeParams), + ) + errorCounter.io.req.valid := false.B + errorCounter.io.req.bits := DontCare + when(readInProgress) { + when(errorCounter.io.req.valid) { + for (i <- 0 until afeParams.mbLanes) { + errorCount(i) := errorCount(i) + errorCounter.io.errorCount(i) + } + } + } + + io.mbRxData.nodeq() + io.sbRxData.nodeq() + val sideband = io.request.bits.pattern === TransmitPattern.CLOCK + when(readInProgress) { + when(sideband) { + io.sbRxData.ready := true.B + when(io.sbRxData.fire) { + val patternToDetect = WireInit( + ("h" + "aaaa" * (sbParams.sbNodeMsgWidth / 16)).U( + sbParams.sbNodeMsgWidth.W, + ), + ) + + /** just do an exact comparison */ + when(io.sbRxData.bits === patternToDetect) { + patternDetectedCount := patternDetectedCount + sbParams.sbNodeMsgWidth.U + } + } + }.otherwise { + io.mbRxData.ready := true.B + when(io.mbRxData.fire) { + errorCounter.io.req.bits.input := io.mbRxData.bits + errorCounter.io.req.bits.pattern := io.request.bits.pattern + errorCounter.io.req.valid := true.B + patternDetectedCount := patternDetectedCount + (afeParams.mbLanes * afeParams.mbSerializerRatio).U + } + } + } +} diff --git a/src/main/scala/logphy/PatternWriter.scala b/src/main/scala/logphy/PatternWriter.scala new file mode 100644 index 00000000..391a3d1f --- /dev/null +++ b/src/main/scala/logphy/PatternWriter.scala @@ -0,0 +1,127 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chisel3.util._ +import sideband.SidebandParams +import interfaces._ + +class PatternWriter( + sbParams: SidebandParams, + afeParams: AfeParams, + maxPatternCount: Int, +) extends Module { + val maxPatternCountWidth = log2Ceil(maxPatternCount + 1) + val io = IO(new Bundle { + val request = Flipped(Valid(new Bundle { + val pattern = TransmitPattern() + val patternCountMax = UInt(maxPatternCountWidth.W) + })) + val resp = Output(new Bundle { + val complete = Bool() + val inProgress = Bool() + }) + val sbTxData = Decoupled(Bits(sbParams.sbNodeMsgWidth.W)) + val mbTxData = + Decoupled(Vec(afeParams.mbLanes, Bits(afeParams.mbSerializerRatio.W))) + }) + + private val writeInProgress = RegInit(false.B) + io.resp.inProgress := writeInProgress + when(io.request.valid && !writeInProgress) { + writeInProgress := true.B + } + + val sbPatternToTransmit = WireInit( + 0.U(sbParams.sbNodeMsgWidth.W), + ) + + val mbPatternToTransmit = VecInit( + Seq.fill(afeParams.mbLanes)(0.U(afeParams.mbSerializerRatio.W)), + ) + + val patternWrittenCount = RegInit(0.U(maxPatternCountWidth.W)) + io.resp.complete := patternWrittenCount >= io.request.bits.patternCountMax + val patternWritten = WireInit(false.B) + + /** Only sideband pattern is clock */ + val sideband = io.request.bits.pattern === TransmitPattern.CLOCK + + io.sbTxData.noenq() + io.mbTxData.noenq() + when(!sideband) { + io.mbTxData.valid := writeInProgress + io.mbTxData.bits := mbPatternToTransmit + when(io.mbTxData.fire) { + patternWrittenCount := patternWrittenCount + (afeParams.mbLanes * afeParams.mbSerializerRatio).U + patternWritten := true.B + } + }.otherwise { + io.sbTxData.valid := writeInProgress + io.sbTxData.bits := sbPatternToTransmit + when(io.sbTxData.fire) { + patternWrittenCount := patternWrittenCount + sbParams.sbNodeMsgWidth.U + patternWritten := true.B + } + + } + + val lfsrPatternGenerator = + Module( + new UCIeScrambler(afeParams, numLanes = afeParams.mbLanes), + ) + lfsrPatternGenerator.io.data_in := VecInit(Seq.fill(afeParams.mbLanes)(0.U)) + lfsrPatternGenerator.io.valid := false.B + + when(writeInProgress) { + switch(io.request.bits.pattern) { + + /** Patterns may be different lengths, etc. so may be best to handle + * separately, for now + */ + + is(TransmitPattern.CLOCK) { + + /** SB clock gating is implemented in the serializer, so just send 128 + * bits of regular clock data TODO: currently not long enough to use in + * MB, if ever used in MB need to make longer + */ + + sbPatternToTransmit := "haaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U + } + is(TransmitPattern.LFSR) { + mbPatternToTransmit := lfsrPatternGenerator.io.data_out + when(patternWritten) { + lfsrPatternGenerator.io.valid := true.B + } + } + is(TransmitPattern.VALTRAIN) { + val valtrain = VecInit( + Seq.fill(afeParams.mbLanes * afeParams.mbSerializerRatio / 8)( + "b1111_0000".U(8.W), + ), + ) + mbPatternToTransmit := valtrain.asTypeOf(mbPatternToTransmit) + } + is(TransmitPattern.PER_LANE_ID) { + val perLaneId = VecInit(Seq.fill(afeParams.mbLanes)(0.U(16.W))) + for (i <- 0 until afeParams.mbLanes) { + perLaneId(i) := Cat("b1010".U(4.W), i.U(8.W), "b1010".U(4.W)) + } + val ratio16 = afeParams.mbSerializerRatio / 16 + val patternVec = VecInit.tabulate(afeParams.mbLanes, ratio16) { + (_, _) => 0.U(16.W) + } + for (i <- 0 until afeParams.mbLanes) { + for (j <- 0 until ratio16) { + patternVec(i)(j) := perLaneId(i) + } + } + mbPatternToTransmit := patternVec.asTypeOf(mbPatternToTransmit) + } + + } + + } + +} diff --git a/src/main/scala/logphy/RdiBringup.scala b/src/main/scala/logphy/RdiBringup.scala index 6c98fc72..a6f6a65a 100644 --- a/src/main/scala/logphy/RdiBringup.scala +++ b/src/main/scala/logphy/RdiBringup.scala @@ -21,7 +21,6 @@ class RdiBringupIO extends Bundle { val plStallReq = Output(Bool()) val lpStallAck = Input(Bool()) - /** TODO: need to support lp link error */ val lpLinkError = Input(Bool()) } @@ -52,6 +51,7 @@ class RdiBringup extends Module { private val nextState = WireInit(state) io.active := state === PhyState.active io.sbTrainIO.msgReq.noenq() + io.sbTrainIO.msgReq.bits.repeat := false.B io.sbTrainIO.msgReqStatus.nodeq() state := nextState when(io.internalError || io.rdiIO.lpLinkError) { @@ -122,7 +122,7 @@ class RdiBringup extends Module { /** TODO: how many timeout cycles here? */ io.sbTrainIO.msgReq.bits.timeoutCycles := 1_000_000.U - when(io.sbTrainIO.msgReqStatus.fire) { + when(io.sbTrainIO.msgReq.fire) { resetSubstate := ResetSubState.RESP_ACTIVE_WAIT } } diff --git a/src/main/scala/logphy/RdiDataMapper.scala b/src/main/scala/logphy/RdiDataMapper.scala index 6c1a0638..cc02377e 100644 --- a/src/main/scala/logphy/RdiDataMapper.scala +++ b/src/main/scala/logphy/RdiDataMapper.scala @@ -29,54 +29,71 @@ class RdiDataMapper( val io = IO(new Bundle { val rdi = Flipped(new RdiDataMapperIO(rdiParams)) - val mainbandLaneIO = Flipped(new MainbandLaneIO(afeParams)) + val mainbandIO = Flipped(new MainbandIO(afeParams)) }) - assert(afeParams.mbSerializerRatio * afeParams.mbLanes < rdiParams.width * 8) + assert(afeParams.mbSerializerRatio * afeParams.mbLanes <= rdiParams.width * 8) /** need to chunk RDI messages, and collect outgoing phy -> d2d */ val afeBits = (afeParams.mbSerializerRatio * afeParams.mbLanes) val ratio = (rdiParams.width * 8) / afeBits - /** collect outgoing phy -> d2d */ - val rxSliceCounter = RegInit(0.U(log2Ceil(ratio).W)) - val rxData = - RegInit( - VecInit( - Seq.fill(ratio)( - 0.U(afeBits.W), + /** RDI has no backpressure mechanism */ + io.mainbandIO.rxData.ready := true.B + + if (afeBits == rdiParams.width * 8) { + io.rdi.lpData.bits <> io.mainbandIO.txData.bits + io.rdi.lpData.ready <> io.mainbandIO.txData.ready + io.mainbandIO.txData.valid := io.rdi.lpData.valid && io.rdi.lpData.irdy + + io.rdi.plData.valid <> io.mainbandIO.rxData.valid + io.rdi.plData.bits <> io.mainbandIO.rxData.bits + + } else { + + /** collect outgoing phy -> d2d */ + val rxSliceCounter = RegInit(0.U(log2Ceil(ratio).W)) + val rxData = + RegInit( + VecInit( + Seq.fill(ratio)( + 0.U(afeBits.W), + ), ), - ), - ) - val hasRxData = RegInit(false.B) - hasRxData := false.B - when(io.mainbandLaneIO.rxData.fire) { - - /** chunk */ - rxData((ratio - 1).U - rxSliceCounter) := io.mainbandLaneIO.rxData.bits - rxSliceCounter := rxSliceCounter + 1.U - when(rxSliceCounter === (ratio - 1).U) { - hasRxData := true.B - rxSliceCounter := 0.U + ) + val hasRxData = RegInit(false.B) + hasRxData := false.B + when(io.mainbandIO.rxData.fire) { + + /** chunk */ + rxData(rxSliceCounter) := io.mainbandIO.rxData.bits + rxSliceCounter := rxSliceCounter + 1.U + when(rxSliceCounter === (ratio - 1).U) { + hasRxData := true.B + rxSliceCounter := 0.U + } } - } - io.rdi.plData.valid := hasRxData - io.rdi.plData.bits := rxData.asUInt - - /** chunk RDI message to transmit */ - private val txWidthCoupler = Module( - new DataWidthCoupler( - DataWidthCouplerParams( - inWidth = rdiParams.width * 8, - outWidth = afeBits, + io.rdi.plData.valid := hasRxData + io.rdi.plData.bits := rxData.asUInt + + /** chunk RDI message to transmit */ + val txWidthCoupler = Module( + new DataWidthCoupler( + DataWidthCouplerParams( + inWidth = rdiParams.width * 8, + outWidth = afeBits, + ), ), - ), - ) - txWidthCoupler.io.out <> io.mainbandLaneIO.txData + ) + io.mainbandIO.txData.bits := txWidthCoupler.io.out.bits.data + io.mainbandIO.txData.valid := txWidthCoupler.io.out.valid + txWidthCoupler.io.out.ready := io.mainbandIO.txData.ready + + io.rdi.lpData.ready := txWidthCoupler.io.in.ready + txWidthCoupler.io.in.valid := io.rdi.lpData.valid & io.rdi.lpData.irdy + txWidthCoupler.io.in.bits := io.rdi.lpData.bits - io.rdi.lpData.ready := txWidthCoupler.io.in.ready - txWidthCoupler.io.in.valid := io.rdi.lpData.valid & io.rdi.lpData.irdy - txWidthCoupler.io.in.bits := io.rdi.lpData.bits + } } diff --git a/src/main/scala/logphy/SBMsgWrapper.scala b/src/main/scala/logphy/SBMsgWrapper.scala index 79a6bbe6..6f9c6e60 100644 --- a/src/main/scala/logphy/SBMsgWrapper.scala +++ b/src/main/scala/logphy/SBMsgWrapper.scala @@ -11,6 +11,84 @@ class SBMsgWrapperTrainIO( val msgReqStatus = Decoupled(new MessageRequestStatus) } +class SBMsgWriter(sbParams: SidebandParams) extends Module { + val io = IO(new Bundle { + val req = Flipped(Valid(new Bundle { + val data = UInt(128.W) + val repeat = Bool() + })) + val result = Valid(MessageRequestStatusType()) + val txData = Decoupled(Bits(sbParams.sbNodeMsgWidth.W)) + }) + val canReceiveReq = RegInit(true.B) + val inProgress = RegInit(false.B) + val complete = RegInit(false.B) + when(io.req.fire && canReceiveReq) { + inProgress := true.B + canReceiveReq := false.B + complete := false.B + } + io.txData.valid := inProgress + io.txData.bits := io.req.bits.data + when(inProgress && io.txData.fire) { + + /** continuously resend */ + complete := true.B + when(!io.req.bits.repeat) { + inProgress := false.B + } + } + io.result.valid := complete || io.txData.fire + io.result.bits := MessageRequestStatusType.SUCCESS +} + +class SBMsgReader(sbParams: SidebandParams) extends Module { + val io = IO(new Bundle { + val req = Flipped(Valid(UInt(128.W))) + val result = Valid(new MessageRequestStatus) + val rxData = Flipped(Decoupled(Bits(sbParams.sbNodeMsgWidth.W))) + }) + + def messageIsEqual(m1: UInt, m2: UInt): Bool = { + + /** opcode */ + (m1(4, 0) === m2(4, 0)) && + /** subcode */ + (m1(21, 14) === m2(21, 14)) && + /** code */ + (m1(39, 32) === m2(39, 32)) + } + + val canReceiveReq = RegInit(true.B) + val inProgress = RegInit(false.B) + val complete = RegInit(false.B) + when(io.req.fire && canReceiveReq) { + inProgress := true.B + complete := false.B + canReceiveReq := false.B + } + + /** if receive message, move on */ + io.rxData.ready := inProgress + val data = RegInit(0.U(64.W)) + val justReceivedMsg = Wire(Bool()) + justReceivedMsg := io.rxData.fire && + messageIsEqual( + io.rxData.bits, + io.req.bits, + ) + + when(inProgress && justReceivedMsg) { + data := io.rxData.bits(127, 64) + complete := true.B + inProgress := false.B + } + + io.result.valid := complete + io.result.bits.status := MessageRequestStatusType.SUCCESS + io.result.bits.data := data +} + class SBMsgWrapper( sbParams: SidebandParams, ) extends Module { @@ -19,150 +97,92 @@ class SBMsgWrapper( val laneIO = Flipped(new SidebandLaneIO(sbParams)) }) + val sbMsgWriter = Module(new SBMsgWriter(sbParams)) + val sbMsgReader = Module(new SBMsgReader(sbParams)) + private object State extends ChiselEnum { - val IDLE, EXCHANGE, WAIT_ACK = Value + val IDLE, EXCHANGE, RECEIVE_ONLY, SEND_ONLY, WAIT_ACK = Value } - // private object SubState extends ChiselEnum { - // val SEND_OR_RECEIVE_MESSAGE, SEND_OR_RECEIVE_DATA = Value - // } - private val currentState = RegInit(State.IDLE) - // private val sendSubState = RegInit(SubState.SEND_OR_RECEIVE_MESSAGE) - // private val receiveSubState = RegInit(SubState.SEND_OR_RECEIVE_MESSAGE) private val timeoutCounter = RegInit(0.U(64.W)) private val nextState = WireInit(currentState) currentState := nextState - private val sentMsg = RegInit(false.B) - private val receivedMsg = RegInit(false.B) + when(currentState =/= nextState) { - // sendSubState := SubState.SEND_OR_RECEIVE_MESSAGE - // receiveSubState := SubState.SEND_OR_RECEIVE_MESSAGE timeoutCounter := 0.U - sentMsg := false.B - receivedMsg := false.B + sbMsgReader.reset := true.B + sbMsgWriter.reset := true.B } private val currentReq = RegInit(0.U((new MessageRequest).msg.getWidth.W)) - // private val currentReqHasData = RegInit(false.B) private val currentReqTimeoutMax = RegInit(0.U(64.W)) private val currentStatus = RegInit(MessageRequestStatusType.ERR) + private val repeat = RegInit(false.B) private val dataOut = RegInit(0.U(64.W)) io.trainIO.msgReqStatus.bits.data := dataOut io.trainIO.msgReqStatus.bits.status := currentStatus - io.laneIO.rxData.nodeq() - io.laneIO.txData.noenq() - io.trainIO.msgReqStatus.noenq() + io.trainIO.msgReqStatus.valid := false.B io.trainIO.msgReq.nodeq() + sbMsgReader.io.rxData <> io.laneIO.rxData + sbMsgWriter.io.txData <> io.laneIO.txData + sbMsgReader.io.req.bits := currentReq + sbMsgWriter.io.req.bits.data := currentReq + sbMsgWriter.io.req.bits.repeat := repeat + sbMsgReader.io.req.valid := false.B + sbMsgWriter.io.req.valid := false.B + private val requestToState = Seq( + MessageRequestType.EXCHANGE -> State.EXCHANGE, + MessageRequestType.SEND -> State.SEND_ONLY, + MessageRequestType.RECEIVE -> State.RECEIVE_ONLY, + ) + switch(currentState) { is(State.IDLE) { io.trainIO.msgReq.ready := true.B when(io.trainIO.msgReq.fire) { currentReq := io.trainIO.msgReq.bits.msg - // currentReqHasData := io.trainIO.msgReq.bits.msgTypeHasData currentReqTimeoutMax := io.trainIO.msgReq.bits.timeoutCycles - nextState := State.EXCHANGE - // switch(io.trainIO.msgReq.bits.reqType) { - // is(MessageRequestType.MSG_REQ) { - // nextState := State.EXCHANGE - // } - // is(MessageRequestType.MSG_RESP) { - // nextState := State.EXCHANGE - // } - // is(MessageRequestType.MSG_EXCH) { - // nextState := State.EXCHANGE - // } - // } + nextState := MuxLookup(io.trainIO.msgReq.bits.reqType, State.EXCHANGE)( + requestToState, + ) + repeat := io.trainIO.msgReq.bits.repeat } } is(State.EXCHANGE) { + sbMsgReader.io.req.valid := true.B + sbMsgWriter.io.req.valid := true.B - /** TODO: incorrect, this logic needs to send message before receiving, - * when in reality both just need to happen - */ - - def messageIsEqual(m1: UInt, m2: UInt): Bool = { - - /** opcode */ - (m1(4, 0) === m2(4, 0)) && - /** subcode */ - (m1(21, 14) === m2(21, 14)) && - /** code */ - (m1(39, 32) === m2(39, 32)) + when(sbMsgWriter.io.result.valid && sbMsgReader.io.result.valid) { + dataOut := sbMsgReader.io.result.bits.data + currentStatus := MessageRequestStatusType.SUCCESS + nextState := State.WAIT_ACK } - /** send message over sideband */ - io.laneIO.txData.valid := true.B - io.laneIO.txData.bits := currentReq - val hasSentMsg = WireInit(io.laneIO.txData.fire || sentMsg) - val justReceivedMsg = Wire(Bool()) - val hasReceivedMsg = Wire(Bool()) - sentMsg := hasSentMsg - - /** if receive message, move on */ - io.laneIO.rxData.ready := true.B - justReceivedMsg := io.laneIO.rxData.fire && - messageIsEqual( - io.laneIO.rxData.bits(64, 0), - currentReq(64, 0), - ) - hasReceivedMsg := justReceivedMsg || receivedMsg - receivedMsg := hasReceivedMsg - - when(hasReceivedMsg && hasSentMsg) { - dataOut := io.laneIO.rxData.bits(127, 64) + /** timeout logic */ + timeoutCounter := timeoutCounter + 1.U + when(timeoutCounter === currentReqTimeoutMax) { + nextState := State.WAIT_ACK + currentStatus := MessageRequestStatusType.ERR + } + } + is(State.RECEIVE_ONLY) { + sbMsgReader.io.req.valid := true.B + when(sbMsgReader.io.result.valid) { + dataOut := sbMsgReader.io.result.bits.data currentStatus := MessageRequestStatusType.SUCCESS nextState := State.WAIT_ACK } + } + is(State.SEND_ONLY) { + sbMsgWriter.io.req.valid := true.B + when(sbMsgWriter.io.result.valid) { + currentStatus := MessageRequestStatusType.SUCCESS + } - // switch(sendSubState) { - // is(SubState.SEND_OR_RECEIVE_MESSAGE) { - // sidebandTxWidthCoupler64.io.in.valid := true.B - // sidebandTxWidthCoupler64.io.in.bits := currentReq(64, 0) - // when(sidebandTxWidthCoupler64.io.in.fire && currentReqHasData) { - // sendSubState := SubState.SEND_OR_RECEIVE_DATA - // } - // } - // is(SubState.SEND_OR_RECEIVE_DATA) { - // sidebandTxWidthCoupler64.io.in.valid := true.B - // sidebandTxWidthCoupler64.io.in.bits := currentReq(128, 64) - // when(sidebandTxWidthCoupler64.io.in.fire) { - // sendSubState := SubState.SEND_OR_RECEIVE_MESSAGE - // } - // } - // } - - // switch(receiveSubState) { - // is(SubState.SEND_OR_RECEIVE_MESSAGE) { - // sidebandRxWidthCoupler64.io.out.ready := true.B - // when(sidebandRxWidthCoupler64.io.out.fire) { - // when( - // messageIsEqual( - // sidebandRxWidthCoupler64.io.out.bits, - // currentReq(64, 0), - // ), - // ) { - // when(currentReqHasData) { - // receiveSubState := SubState.SEND_OR_RECEIVE_DATA - // }.otherwise { - // nextState := State.WAIT_ACK_SUCCESS - // } - // } - // } - // } - // is(SubState.SEND_OR_RECEIVE_DATA) { - // sidebandRxWidthCoupler64.io.out.ready := true.B - // when(sidebandRxWidthCoupler64.io.out.fire) { - // dataOut := sidebandRxWidthCoupler64.io.out.bits - // nextState := State.WAIT_ACK_SUCCESS - // } - // } - // } - - /** timeout logic */ timeoutCounter := timeoutCounter + 1.U when(timeoutCounter === currentReqTimeoutMax) { nextState := State.WAIT_ACK @@ -171,7 +191,6 @@ class SBMsgWrapper( } is(State.WAIT_ACK) { - printf("ack\n") io.trainIO.msgReqStatus.valid := true.B when(io.trainIO.msgReqStatus.fire) { nextState := State.IDLE diff --git a/src/main/scala/Scrambler.scala b/src/main/scala/logphy/Scrambler.scala similarity index 58% rename from src/main/scala/Scrambler.scala rename to src/main/scala/logphy/Scrambler.scala index 24557578..b96aa116 100644 --- a/src/main/scala/Scrambler.scala +++ b/src/main/scala/logphy/Scrambler.scala @@ -1,12 +1,13 @@ package edu.berkeley.cs.ucie.digital -package interfaces +package logphy +import chisel3.util._ import chisel3._ import chisel3.util.random._ +import edu.berkeley.cs.ucie.digital.interfaces.AfeParams class Scrambler( afeParams: AfeParams, - width: Int, seed: BigInt, ) extends Module { val io = IO(new Bundle { @@ -16,24 +17,26 @@ class Scrambler( val data_out = Output(UInt(afeParams.mbSerializerRatio.W)) }) val LFSR = Module( - new FibonacciLFSR( + new ConsistentFibonacciLFSR( 23, - Set(23, 21, 18, 15, 7, 2, 1), + Seq(23, 21, 16, 8, 5, 2), Some(seed), XOR, - width, + afeParams.mbSerializerRatio, false, ), ) LFSR.io.increment := io.valid LFSR.io.seed.bits := VecInit(io.seed.asBools) LFSR.io.seed.valid := (reset.asBool) - io.data_out := LFSR.io.out.asUInt ^ io.data_in + val LFSR_flipped = Wire(UInt(16.W)) + LFSR_flipped := Reverse(LFSR.io.out.asUInt(22, 23 - 16)) + + io.data_out := LFSR_flipped ^ io.data_in } class UCIeScrambler( afeParams: AfeParams, - width: Int, numLanes: Int, ) extends Module { val io = IO(new Bundle { @@ -42,24 +45,25 @@ class UCIeScrambler( val data_out = Output(Vec(numLanes, UInt(afeParams.mbSerializerRatio.W))) }) val UCIe_seeds = List( - "1dbfbc", - "0607bb", - "1ec760", - "18c0db", - "010f12", - "19cfc9", - "0277ce", - "1bb807", + /** seeds have to be reversed so that LSB ends up in rightmost position */ + "00111101111111011011100", // "1dbfbc", + "11011101111000000110000", // "0607bb", + "00000110111000110111100", // "1ec760", + "11011011000000110001100", // "18c0db", + "01001000111100001000000", // "010f12", + "10010011111100111001100", // "19cfc9", + "01110011111011100100000", // "0277ce", + "11100000000111011101100", // "1bb807", ) val seeds = (for (i <- 0 until numLanes) yield UCIe_seeds(i % 8)).toList val scramblers = - seeds.map(seed => Module(new Scrambler(afeParams, width, BigInt(seed, 16)))) + seeds.map(seed => Module(new Scrambler(afeParams, BigInt(seed, 2)))) for (i <- 0 until scramblers.length) { scramblers(i).io.data_in := io.data_in(i) scramblers(i).io.valid := io.valid scramblers(i).reset := reset scramblers(i).clock := clock - scramblers(i).io.seed := ("h" + seeds(i)).U(23.W) + scramblers(i).io.seed := ("b" + seeds(i)).U(23.W) io.data_out(i) := scramblers(i).io.data_out } } diff --git a/src/main/scala/mbafe/AsyncFifoCustom.scala b/src/main/scala/mbafe/AsyncFifoCustom.scala new file mode 100644 index 00000000..7b156d4a --- /dev/null +++ b/src/main/scala/mbafe/AsyncFifoCustom.scala @@ -0,0 +1,51 @@ +package edu.berkeley.cs.ucie.digital +package afe + +import chisel3._ +import chisel3.util._ + + + +class AsyncFifoCustom(depth: Int, width: Int) extends Module { + val io = IO(new Bundle { + val deq_clock = Input(Clock()) + val deq = Flipped(Flipped(Decoupled(UInt(width.W)))) + val deq_reset = Input(Bool()) + val enq_reset = Input(Bool()) + val enq_clock = Input(Clock()) + val enq = Flipped(Decoupled(UInt(width.W))) + }) + val fifo_inst = Module (new AsyncFifoCustomCore(depth, width)) + + fifo_inst.io.rst := ~io.enq_reset + fifo_inst.io.clk_w := io.enq_clock + fifo_inst.io.valid_w := io.enq.valid + io.enq.ready := fifo_inst.io.ready_w + fifo_inst.io.data_w := io.enq.bits + + // fifo_inst.io.clk_r := io.deq_clock + // fifo_inst.io.ready_r := io.deq.ready + // io.deq.bits := fifo_inst.io.data_r + // io.deq.valid := fifo_inst.io.valid_r + io.deq.valid := fifo_inst.io.valid_r + fifo_inst.io.clk_r := io.deq_clock + fifo_inst.io.ready_r := io.deq.ready + io.deq.bits := fifo_inst.io.data_r +} + +class AsyncFifoCustomCore (depth: Int, width: Int) extends BlackBox( + Map("DEPTH" -> depth, "WIDTH" -> width) +) with HasBlackBoxResource { + val io = IO(new Bundle { + val rst = Input(Bool()) + val clk_w = Input(Clock()) + val valid_w = Input(Bool()) + val ready_w = Output(Bool()) + val data_w = Input(Bits(width.W)) + val clk_r = Input(Clock()) + val valid_r = Output(Bool()) + val ready_r = Input(Bool()) + val data_r = Output(Bits(width.W)) + }) + addResource("/vsrc/AsyncFifoCustomCore.sv") +} \ No newline at end of file diff --git a/src/main/scala/mbafe/MbAfe.scala b/src/main/scala/mbafe/MbAfe.scala new file mode 100644 index 00000000..383d15ed --- /dev/null +++ b/src/main/scala/mbafe/MbAfe.scala @@ -0,0 +1,107 @@ +package edu.berkeley.cs.ucie.digital +package afe + +import chisel3._ +import chisel3.util.{Decoupled, Counter, Reverse} +import circt.stage.ChiselStage +import interfaces._ +import chisel3.reflect.DataMirror +import freechips.rocketchip.util.{AsyncQueue, AsyncQueueParams} + +// This module receives data from logphy and sends to analog +class TxMainbandSerializer(afeParams: AfeParams) extends Module { + val io = IO(new Bundle { + val txInData = Flipped( + Decoupled(Vec(afeParams.mbLanes, Bits(afeParams.mbSerializerRatio.W))), + ) + val txMbIo = Output(new MainbandIo()) + }) + + val lanes = afeParams.mbLanes + val width = afeParams.mbSerializerRatio + val hasData = Wire(Bool()) + hasData := io.txInData.valid + + // receive data + + val txMbShiftRegs = Seq.fill(lanes)(RegInit(0.U(width.W))) + val sending = RegInit(false.B) + val (txMbUICounter, uiCountDone) = Counter(sending, width) + + io.txInData.ready := !sending + when(io.txInData.fire) { + sending := true.B + }.elsewhen(uiCountDone) { + sending := false.B + } + + val shift = RegInit(false.B) + + io.txMbIo.clkn := (!clock.asBool).asClock + io.txMbIo.clkp := clock + + // Assign each async fifo individually + io.txInData.bits.zipWithIndex.foreach { case (laneData, i) => + // Valid framing, up for first 4 ui, down for last 4 ui + when(io.txInData.fire) { + txMbShiftRegs(i) := laneData + }.otherwise { + txMbShiftRegs(i) := txMbShiftRegs(i) >> 1.U + } + } + + io.txMbIo.data := VecInit(txMbShiftRegs.map(_(0))).asUInt + io.txMbIo.valid := sending + io.txMbIo.track := false.B +} + +// This module accepts data from analog and send to adapter +class RxMainbandDeserializer( + afeParams: AfeParams, +) extends Module { + val io = IO(new Bundle { + val rxMbIo = Input(new MainbandIo()) + val rxOutData = + Decoupled(Vec(afeParams.mbLanes, Bits(afeParams.mbSerializerRatio.W))) + }) + + private val width = afeParams.mbSerializerRatio + private val lanes = afeParams.mbLanes + + val rxMbShiftRegs = Seq.fill(lanes)(RegInit(0.U(width.W))) + val (_, done_sending) = Counter(io.rxMbIo.valid, width) + val out_valid = RegNext(done_sending) + + rxMbShiftRegs.zipWithIndex.foreach { case (rxMbShiftReg, i) => + when(io.rxMbIo.valid) { + rxMbShiftReg := (rxMbShiftReg << 1.U) | io.rxMbIo.data(i) + } + io.rxOutData.bits(i) := Reverse(rxMbShiftReg) + } + io.rxOutData.valid := out_valid +} + +class MbAfe(afeParams: AfeParams) extends Module { + val io = IO(new Bundle { + val mbAfeIo = Flipped(new MainbandAfeIo(afeParams)) + val mbTxData = Output(new MainbandIo(afeParams.mbLanes)) + val mbRxData = Input(new MainbandIo(afeParams.mbLanes)) + }) + + val txMainband = Module(new TxMainbandSerializer(afeParams)) + val rxMainband = + withClockAndReset(io.mbRxData.clkp, reset.asAsyncReset)( + Module(new RxMainbandDeserializer(afeParams)), + ) + + /** Connect to LogPhy AFE IO */ + io.mbAfeIo.fifoParams.clk := io.mbRxData.clkp + io.mbAfeIo.fifoParams.reset := reset.asBool + io.mbAfeIo.txData <> txMainband.io.txInData + io.mbAfeIo.rxData <> rxMainband.io.rxOutData + io.mbAfeIo.pllLock := true.B + + /** Connect to Mainband IO */ + io.mbTxData <> txMainband.io.txMbIo + io.mbRxData <> rxMainband.io.rxMbIo +} diff --git a/src/main/scala/protocol/ProtocolLayer.scala b/src/main/scala/protocol/ProtocolLayer.scala index 1fc72fa8..5fd15dee 100644 --- a/src/main/scala/protocol/ProtocolLayer.scala +++ b/src/main/scala/protocol/ProtocolLayer.scala @@ -67,16 +67,18 @@ class ProtocolLayer(val fdiParams: FdiParams) extends Module { // io.fdi.plStateStatus === PhyState.active) val lp_rx_active_pl_state = (io.fdi.plStateStatus === PhyState.active) - when(io.fdi.plRxActiveReq && io.TLready_to_rcv && lp_rx_active_pl_state) { + when(io.fdi.plRxActiveReq && io.TLready_to_rcv){//&& lp_rx_active_pl_state) { lp_rx_active_sts_reg := true.B } io.fdi.lpRxActiveStatus := lp_rx_active_sts_reg // Refer to section 8.2.8 for FDI bringup and state req logic val lp_state_req_reg = RegInit(PhyStateReq.nop) + //val lp_state_req_prev = RegInit(PhyState.nop) + //lp_state_req_prev := io.fdi.plStateStatus + // Removed: lp_state_req_prev === PhyState.nop & val reqActive = ((io.fdi.plStateStatus === PhyState.reset & - lp_state_req_reg === PhyStateReq.nop & io.fdi.plInbandPres) || io.fdi.plStateStatus === PhyState.linkReset) when(reqActive) { diff --git a/src/main/scala/sideband/sb-msg-encoding.scala b/src/main/scala/sideband/sb-msg-encoding.scala index 9409c69b..1fe9a855 100644 --- a/src/main/scala/sideband/sb-msg-encoding.scala +++ b/src/main/scala/sideband/sb-msg-encoding.scala @@ -352,6 +352,27 @@ object SBM { def MBINIT_PARAM_CONFIG_RESP = BitPat( "b????????????????????????????????????????????????????????????????????????000000000000000010101010??????????00000000?????????11011", ) + def MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_REQ = BitPat( + "b????????????????????????????????????????????????????????????????????????????????????????00000001??????????10000101?????????00000", + ) + def MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_RESP = BitPat( + "b????????????????????????????????????????????????????????????????????????????????????????00000001??????????10001010?????????00000", + ) + + def MBTRAIN_TX_INIT_D_TO_C_RESULTS_REQ = BitPat( + "b????????????????????????????????????????????????????????????????????????????????????????00000011??????????10000101?????????00000", + ) + def MBTRAIN_TX_INIT_D_TO_C_RESULTS_RESP = BitPat( + "b????????????????????????????????????????????????????????????????????????????????????????00000011??????????10001010?????????00000", + ) + + def MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_REQ = BitPat( + "b????????????????????????????????????????????????????????????????????????????????????????00000100??????????10000101?????????00000", + ) + def MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_RESP = BitPat( + "b????????????????????????????????????????????????????????????????????????????????????????00000100??????????10001010?????????00000", + ) + def MBTRAIN_VALVREF_START_REQ = BitPat( "b????????????????????????????????????????????????????????????????????????????????????????00000000??????????10110101?????????00000", ) @@ -402,7 +423,7 @@ object SBMessage_factory { dst_num = dst_num << 26 msg += dst_num // println("SBMessage_factory: " + msg) - val new_msg = Cat(data, (msg.U(64.W) | (msgInfo << (32 + 8).U))) + val new_msg = Cat(data, (msg.U(64.W) | msgInfo << (32 + 8).U)(63, 0)) // printf("SBMessage_factory: %x\n", new_msg) new_msg } @@ -411,8 +432,8 @@ object SBMessage_factory { src: String, remote: Boolean, dst: String, - data: Int, - msgInfo: Int, + data: BigInt, + msgInfo: BigInt, ): BigInt = { // take the bottom 64 bits of the base by modulo var msg: BigInt = base.value % (BigInt(1) << 64) diff --git a/src/main/scala/sideband/sidebandChannel.scala b/src/main/scala/sideband/sidebandChannel.scala index 734ad518..2bf37825 100644 --- a/src/main/scala/sideband/sidebandChannel.scala +++ b/src/main/scala/sideband/sidebandChannel.scala @@ -57,9 +57,22 @@ class PHYSidebandChannel( switcher.io.outer.layer_to_node_below.nodeq() lower_node.io.inner.layer_to_node <> io.inner.rawInput } - lower_node.io.inner.node_to_layer <> switcher.io.outer.node_to_layer_below + + when(io.inner.rxMode === RXTXMode.RAW) { + switcher.io.outer.node_to_layer_below.noenq() + switcher.io.inner.node_to_layer_below.nodeq() + + lower_node.io.inner.node_to_layer <> io.inner.switcherBundle.node_to_layer_below + switcher.io.inner.layer_to_node_below <> io.inner.switcherBundle.layer_to_node_below + switcher.io.inner.node_to_layer_above <> io.inner.switcherBundle.node_to_layer_above + switcher.io.inner.layer_to_node_above <> io.inner.switcherBundle.layer_to_node_above + + }.otherwise { + lower_node.io.inner.node_to_layer <> switcher.io.outer.node_to_layer_below + io.inner.switcherBundle <> switcher.io.inner + } lower_node.io.rxMode := io.inner.rxMode // Connect inner signals - io.inner.switcherBundle <> switcher.io.inner + // io.inner.switcherBundle <> switcher.io.inner } diff --git a/src/main/scala/sideband/sidebandNode.scala b/src/main/scala/sideband/sidebandNode.scala index f1d154ad..973b65b1 100644 --- a/src/main/scala/sideband/sidebandNode.scala +++ b/src/main/scala/sideband/sidebandNode.scala @@ -2,8 +2,9 @@ package edu.berkeley.cs.ucie.digital package sideband import chisel3._ +import chisel3.experimental._ import chisel3.util._ - +import freechips.rocketchip.util.{AsyncQueue, AsyncResetReg, ClockGate} import interfaces._ //TODO: 1) L317-318 needs to be revisited @@ -262,12 +263,17 @@ class SidebandLinkSerializer( val sending = RegInit(false.B) val done = RegInit(false.B) val waited = RegInit(true.B) + val sendNext = RegInit(false.B) val (sendCount, sendDone) = Counter(sending, dataBeats) val isComplete = RegInit(false.B) io.in.ready := (waited) // wait for 32 cycles between SB messages - io.out.clock := Mux(sending, clock.asUInt, false.B) + + val sendNegEdge = withClock((!clock.asBool).asClock)(RegInit(false.B)) + sendNegEdge := sendNext || sending && (sendCount =/= (dataBeats - 1).U) + + io.out.clock := Mux(sendNegEdge, clock.asUInt, false.B) io.out.bits := data(sb_w - 1, 0) counter_en := false.B @@ -276,9 +282,15 @@ class SidebandLinkSerializer( when(io.in.fire) { data := io.in.bits.asUInt + sendNext := true.B + } + + when(sendNext) { + sendNext := false.B sending := true.B - waited := false.B counter_next := 0.U + done := false.B + waited := false.B } when(sending) { data := data >> sb_w.U } @@ -312,22 +324,44 @@ class SidebandLinkDeserializer( val dataBits = msg_w val dataBeats = (dataBits - 1) / sb_w + 1 - val data = Reg(Vec(dataBeats, UInt(sb_w.W))) + val sbDeserBlackBox = Module(new SBDeserializerBlackBox(msg_w)) - val receiving = RegInit(true.B) + val valid_sync_1 = RegNext(sbDeserBlackBox.io.out_data_valid) + val valid_sync = RegNext(valid_sync_1) + val data_sync_1 = RegNext(sbDeserBlackBox.io.out_data) + val data_sync = RegNext(data_sync_1) - withClock(remote_clock) { + val flag = RegInit(true.B) + when(valid_sync) { + flag := false.B + }.otherwise { + flag := true.B + } - val (recvCount, recvDone) = Counter(true.B, dataBeats) + io.out.valid := valid_sync && flag + io.out.bits := data_sync - val recvCount_delay = RegInit(0.U(log2Ceil(dataBeats).W)) - recvCount_delay := recvCount + sbDeserBlackBox.io.clk := remote_clock + sbDeserBlackBox.io.rst := reset.asAsyncReset + sbDeserBlackBox.io.in_data := io.in.bits - data(recvCount_delay) := io.in.bits - when(recvDone) { receiving := false.B } - when(io.out.fire) { receiving := true.B } - io.out.valid := !receiving +} - io.out.bits := data.asUInt - } +class SBDeserializerBlackBox(val width: Int) + extends BlackBox( + Map( + "WIDTH" -> IntParam(width), + "WIDTH_W" -> IntParam(log2Ceil(width) + 1), + ), + ) + with HasBlackBoxResource { + val io = IO(new Bundle { + val clk = Input(Clock()) + val rst = Input(Reset()) + val in_data = Input(UInt(1.W)) + val out_data = Output(UInt(width.W)) + val out_data_valid = Output(Bool()) + }) + + addResource("/vsrc/SBDeserializer.v") } diff --git a/src/main/scala/tilelink/Common.scala b/src/main/scala/tilelink/Common.scala index 3fb71c22..1b1dea58 100644 --- a/src/main/scala/tilelink/Common.scala +++ b/src/main/scala/tilelink/Common.scala @@ -23,19 +23,20 @@ case class TileLinkParams( val addressRange: BigInt, val configAddress: BigInt, val inwardQueueDepth: Int, - val outwardQueueDepth: Int + val outwardQueueDepth: Int, + val dataWidth_arg: Int = 256, ) { val CONFIG_ADDRESS = configAddress val ADDRESS = address val ADDR_RANGE = addressRange - val BEAT_BYTES = 32 // 256 bits/8 - val CONFIG_BEAT_BYTES = 8 + val BEAT_BYTES = dataWidth_arg/8 // 32 // 256 bits/8 + val CONFIG_BEAT_BYTES = 32 val opcodeWidth = 3 val paramWidth = 3 val sourceIDWidth = 8 val sinkIDWidth = 8 val addressWidth = 64 - val dataWidth = 256 + val dataWidth = dataWidth_arg val maskWidth = dataWidth/8 val sizeWidth = log2Ceil(dataWidth/8) // 5 bits if 256 val deniedWidth = 1 diff --git a/src/main/scala/tilelink/Configs.scala b/src/main/scala/tilelink/Configs.scala index 675cb2e1..956dda22 100644 --- a/src/main/scala/tilelink/Configs.scala +++ b/src/main/scala/tilelink/Configs.scala @@ -8,6 +8,7 @@ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.tilelink._ import org.chipsalliance.cde.config.{Field, Config, Parameters} import freechips.rocketchip.subsystem._ +import testchipip.soc.{OBUS} //import freechips.rocketchip.subsystem.{BaseSubsystem, CacheBlockBytes} import freechips.rocketchip.regmapper.{HasRegMap, RegField} import interfaces._ @@ -15,43 +16,58 @@ import protocol._ import sideband.{SidebandParams} import logphy.{LinkTrainingParams} -case class UCITLParams ( - val protoParams: ProtocolLayerParams, - val tlParams: TileLinkParams, - val fdiParams: FdiParams, - val rdiParams: RdiParams, - val sbParams: SidebandParams, - val myId: BigInt, - val linkTrainingParams: LinkTrainingParams, - val afeParams: AfeParams, - val laneAsyncQueueParams: AsyncQueueParams +case class UCITLParams( + val protoParams: ProtocolLayerParams, + val tlParams: TileLinkParams, + val fdiParams: FdiParams, + val rdiParams: RdiParams, + val sbParams: SidebandParams, + val linkTrainingParams: LinkTrainingParams, + val afeParams: AfeParams, + val laneAsyncQueueParams: AsyncQueueParams, ) case object UCITLKey extends Field[Option[UCITLParams]](None) trait CanHaveTLUCIAdapter { this: BaseSubsystem => - p(UCITLKey).map { params => - val bus = locateTLBusWrapper(SBUS) //TODO: make parameterizable? - val uciTL = LazyModule(new UCITLFront( - tlParams = params.tlParams, - protoParams = params.protoParams, - fdiParams = params.fdiParams, - rdiParams = params.rdiParams, - sbParams = params.sbParams, - myId = params.myId, - linkTrainingParams = params.linkTrainingParams, - afeParams = params.afeParams, - laneAsyncQueueParams = params.laneAsyncQueueParams - )) - uciTL.clockNode := bus.fixedClockNode - bus.coupleTo(s"ucie_tl_man_port") { - uciTL.managerNode := TLWidthWidget(bus.beatBytes) := TLSourceShrinker(params.tlParams.sourceIDWidth) := TLFragmenter(bus.beatBytes, p(CacheBlockBytes)) := _ - } //manager node because SBUS is making request? - bus.coupleFrom(s"ucie_tl_cl_port") { _ := TLWidthWidget(bus.beatBytes) := uciTL.clientNode } - bus.coupleTo(s"ucie_tl_ctrl_port") { uciTL.regNode.node := TLWidthWidget(bus.beatBytes) := TLFragmenter(bus.beatBytes, bus.blockBytes) := _ } + val uciTL = p(UCITLKey) match { + case Some(params) => { + val obus = locateTLBusWrapper(OBUS) // TODO: make parameterizable? + val sbus = locateTLBusWrapper(SBUS) + val uciTL = LazyModule( + new UCITLFront( + tlParams = params.tlParams, + protoParams = params.protoParams, + fdiParams = params.fdiParams, + rdiParams = params.rdiParams, + sbParams = params.sbParams, + // myId = params.myId, + linkTrainingParams = params.linkTrainingParams, + afeParams = params.afeParams, + laneAsyncQueueParams = params.laneAsyncQueueParams, + ), + ) + + uciTL.clockNode := sbus.fixedClockNode + obus.coupleTo(s"ucie_tl_man_port") { + uciTL.managerNode := TLWidthWidget(obus.beatBytes) := TLBuffer() := TLSourceShrinker(params.tlParams.sourceIDWidth) := TLFragmenter(obus.beatBytes, p(CacheBlockBytes)) := TLBuffer() := _ + } //manager node because SBUS is making request? + sbus.coupleFrom(s"ucie_tl_cl_port") { _ := TLBuffer() := TLWidthWidget(sbus.beatBytes) := TLBuffer() := uciTL.clientNode } + sbus.coupleTo(s"ucie_tl_ctrl_port") { uciTL.regNode.node := TLWidthWidget(sbus.beatBytes) := TLFragmenter(sbus.beatBytes, sbus.blockBytes) := TLBuffer() := _ } + Some(uciTL) + } + case None => None + } + val ucie_io = uciTL.map { uci => + InModuleBody { + val ucie_io = IO(new UcieDigitalTopIO()) + ucie_io <> uci.module.io + ucie_io + } } } -class WithUCITLAdapter(params: UCITLParams) extends Config((site, here, up) => { - case UCITLKey => Some(params) -}) \ No newline at end of file +class WithUCITLAdapter(params: UCITLParams) + extends Config((site, here, up) => { case UCITLKey => + Some(params) + }) diff --git a/src/main/scala/tilelink/UCITLFront.scala b/src/main/scala/tilelink/UCITLFront.scala index bb0afac7..006262fd 100644 --- a/src/main/scala/tilelink/UCITLFront.scala +++ b/src/main/scala/tilelink/UCITLFront.scala @@ -15,376 +15,513 @@ import e2e._ import protocol._ import interfaces._ import sideband._ -import logphy.{LinkTrainingParams} +import logphy._ + +class UcieDigitalTopIO(mbLanes: Int = 16, STANDALONE: Boolean = true) + extends Bundle { + //  FDI interface for testing purposes only + // val fdi = new Fdi(fdiParams) + // IOs for connecting to the AFE + val mbSerializerRatio = if (STANDALONE) 16 else 32 + val afeParams = + AfeParams(mbSerializerRatio = mbSerializerRatio, STANDALONE = STANDALONE) + val linkTrainingParams = LinkTrainingParams() + val mbAfe_tx = if (STANDALONE) Some(Output(new MainbandIo(mbLanes))) else None + val mbAfe_rx = if (STANDALONE) Some(Input(new MainbandIo(mbLanes))) else None + val phyAfe = + if (STANDALONE) None else Some(new MainbandLaneIOWithValid(afeParams)) + val rxSbAfe = Input(new SidebandIo()) + val txSbAfe = Output(new SidebandIo()) + val train = + if (STANDALONE) None + else + Some(new TrainingOperation(afeParams, linkTrainingParams.maxPatternCount)) + // val mbAfe = new MainbandAfeIo(afeParams) + // val sbAfe = new SidebandAfeIo(afeParams) +} // TODO: Sideband messaging -/** Main class to generate manager, client and register nodes on the tilelink diplomacy. - * These needs to get connected to the chipyard system. The class converts tilelink - * packets to UCIe Raw 64B flit. It also instantiates the protocol layer which acts as - * an agnostic interface to generate FDI signalling. +/** Main class to generate manager, client and register nodes on the tilelink + * diplomacy. These needs to get connected to the chipyard system. The class + * converts tilelink packets to UCIe Raw 64B flit. It also instantiates the + * protocol layer which acts as an agnostic interface to generate FDI + * signalling. */ -class UCITLFront(val tlParams: TileLinkParams, val protoParams: ProtocolLayerParams, - val fdiParams: FdiParams, val rdiParams: RdiParams, - val sbParams: SidebandParams, val myId: BigInt, - val linkTrainingParams: LinkTrainingParams, - val afeParams: AfeParams, - val laneAsyncQueueParams: AsyncQueueParams) - (implicit p: Parameters) extends ClockSinkDomain(ClockSinkParameters())(p) { - - val device = new SimpleDevice("ucie-front", Seq("ucie,ucie0")) +class UCITLFront( + val tlParams: TileLinkParams, + val protoParams: ProtocolLayerParams, + val fdiParams: FdiParams, + val rdiParams: RdiParams, + val sbParams: SidebandParams, + val linkTrainingParams: LinkTrainingParams, + val afeParams: AfeParams, + val laneAsyncQueueParams: AsyncQueueParams, +)(implicit p: Parameters) + extends ClockSinkDomain(ClockSinkParameters())(p) { + override lazy val desiredName = "UCITLFront" + + val device = new SimpleDevice("ucie-front", Seq("ucie,ucie0")) val beatBytes = tlParams.BEAT_BYTES // MMIO registers controlled by the sideband module - val regNode = LazyModule(new UCIConfigRF(beatBytes = tlParams.CONFIG_BEAT_BYTES, address = tlParams.CONFIG_ADDRESS)) - + val regNode = LazyModule( + new UCIConfigRF( + beatBytes = tlParams.CONFIG_BEAT_BYTES, + address = tlParams.CONFIG_ADDRESS, + ), + ) + // Manager node to send and acquire traffic to partner die - val managerNode: TLManagerNode = TLManagerNode(Seq(TLSlavePortParameters.v1( - Seq(TLSlaveParameters.v1( - address = Seq(AddressSet(tlParams.ADDRESS, tlParams.ADDR_RANGE)), - resources = device.reg, - regionType = RegionType.UNCACHED, // Should be changed to CACHED eventually - executable = true, - supportsGet = TransferSizes(1, beatBytes), - supportsPutFull = TransferSizes(1, beatBytes), - supportsPutPartial = TransferSizes(1, beatBytes), - fifoId = Some(0))), - beatBytes = beatBytes))) + val managerNode: TLManagerNode = TLManagerNode( + Seq( + TLSlavePortParameters.v1( + Seq( + TLSlaveParameters.v1( + address = Seq(AddressSet(tlParams.ADDRESS, tlParams.ADDR_RANGE)), + resources = device.reg, + regionType = + RegionType.UNCACHED, // Should be changed to CACHED eventually + executable = true, + supportsGet = TransferSizes(1, beatBytes), + supportsPutFull = TransferSizes(1, beatBytes), + supportsPutPartial = TransferSizes(1, beatBytes), + fifoId = Some(0), + ), + ), + beatBytes = beatBytes, + ), + ), + ) // Client node to reply to send and acquire traffic from partner die - val clientNode: TLClientNode = TLClientNode(Seq(TLMasterPortParameters.v1( - Seq(TLMasterParameters.v1( - name = "ucie-client", - sourceId = IdRange(0, 1 << tlParams.sourceIDWidth), - requestFifo = true, - visibility = Seq(AddressSet(tlParams.ADDRESS, tlParams.ADDR_RANGE)) - ))))) + val clientNode: TLClientNode = TLClientNode( + Seq( + TLMasterPortParameters.v1( + Seq( + TLMasterParameters.v1( + name = "ucie-client", + sourceId = IdRange(0, 1 << tlParams.sourceIDWidth), + requestFifo = true, + visibility = Seq(AddressSet(tlParams.ADDRESS, tlParams.ADDR_RANGE)), + ), + ), + ), + ), + ) + + /* val topIO = BundleBridgeSource(() => new + * UcieDigitalTopIO(afeParams.mbLanes)) */ override lazy val module = new UCITLFrontImp -class UCITLFrontImp extends Impl { - val io = IO(new Bundle { - // FDI interface for testing purposes only - //val fdi = new Fdi(fdiParams) - // IOs for connecting to the AFE - val mbAfe = new MainbandAfeIo(afeParams) - val sbAfe = new SidebandAfeIo(afeParams) - }) - withClockAndReset(clock, reset) { - - withClockAndReset(clock, reset) { - - val fault = RegInit(false.B) // if fault in ecc code - - // Instantiate the agnostic protocol layer - //val protocol = Module(new ProtocolLayer(fdiParams)) - val ucietop = Module(new UCITop(fdiParams, rdiParams, - sbParams, myId, - linkTrainingParams, - afeParams, laneAsyncQueueParams)) - //io.fdi <> ucietop.io.fdi - ucietop.io.fault := fault - io.mbAfe <> ucietop.io.mbAfe - io.sbAfe <> ucietop.io.sbAfe - - // Hamming encode and decode - val hammingEncoder = Module(new HammingEncode(protoParams)) - val hammingDecoder = Module(new HammingDecode(protoParams)) - // Defaults - hammingEncoder.io.data := 0.U - hammingDecoder.io.data := 0.U - hammingDecoder.io.checksum := 0.U - - //Sideband node for protocol layer - val protocol_sb_node = Module(new SidebandNode((new SidebandParams), fdiParams)) - - protocol_sb_node.io.outer.rx.bits := ucietop.io.fdi_lpConfig.bits - protocol_sb_node.io.outer.rx.valid := ucietop.io.fdi_lpConfig.valid - ucietop.io.fdi_lpConfigCredit := protocol_sb_node.io.outer.rx.credit - - ucietop.io.fdi_plConfig.bits := protocol_sb_node.io.outer.tx.bits - ucietop.io.fdi_plConfig.valid := protocol_sb_node.io.outer.tx.valid - protocol_sb_node.io.outer.tx.credit := ucietop.io.fdi_plConfigCredit - - protocol_sb_node.io.inner.layer_to_node.bits := Cat(regNode.module.io.sb_csrs.sideband_mailbox_sw_to_node_data_high, - regNode.module.io.sb_csrs.sideband_mailbox_sw_to_node_data_low, - regNode.module.io.sb_csrs.sidebank_mailbox_sw_to_node_index_high, - regNode.module.io.sb_csrs.sideband_mailbox_sw_to_node_index_low) - protocol_sb_node.io.inner.layer_to_node.valid := regNode.module.io.sb_csrs.sideband_mailbox_sw_valid - - regNode.module.io.sb_csrs.sideband_mailbox_index_low := protocol_sb_node.io.inner.node_to_layer.bits(31, 0) - regNode.module.io.sb_csrs.sideband_mailbox_index_high := protocol_sb_node.io.inner.node_to_layer.bits(63, 32) - regNode.module.io.sb_csrs.sideband_mailbox_data_low := protocol_sb_node.io.inner.node_to_layer.bits(95, 64) - regNode.module.io.sb_csrs.sideband_mailbox_data_high := protocol_sb_node.io.inner.node_to_layer.bits(127, 96) - protocol_sb_node.io.inner.node_to_layer.ready := regNode.module.io.sb_csrs.sideband_mailbox_ready - regNode.module.io.sb_csrs.sideband_mailbox_valid := protocol_sb_node.io.inner.node_to_layer.valid - - val tlBundleParams = new TLBundleParameters(addressBits = tlParams.addressWidth, - dataBits = tlParams.dataWidth, - sourceBits = tlParams.sourceIDWidth, - sinkBits = tlParams.sinkIDWidth, - sizeBits = tlParams.sizeWidth, - echoFields = Nil, - requestFields = Nil, - responseFields = Nil, - hasBCE = false) - - val client_tl = clientNode.out(0)._1 - val client_edge = clientNode.out(0)._2 - val manager_tl = managerNode.in(0)._1 - val manager_edge = managerNode.in(0)._2 - - val clientParams = client_edge.bundle - val managerParams = manager_edge.bundle - val mergedParams = clientParams.union(managerParams).union(tlBundleParams) - require(mergedParams.echoFields.isEmpty, "UCIe does not support TileLink with echo fields") - require(mergedParams.requestFields.isEmpty, "UCIe does not support TileLink with request fields") - require(mergedParams.responseFields.isEmpty, "UCIe does not support TileLink with response fields") - require(mergedParams == tlBundleParams, s"UCIe is misconfigured, the combined inwards/outwards parameters cannot be serialized using the provided bundle params\n$mergedParams > $tlBundleParams") - - val inwardA = Module(new Queue((new TLBundleA(mergedParams)), 16, pipe=true, flow=true)) - val inwardD = Module(new Queue((new TLBundleD(mergedParams)), 16, pipe=true, flow=true)) - - val outwardA = Module(new CreditedToDecoupledMsg(new TLBundleA(mergedParams), 4, 16)) - val outwardD = Module(new CreditedToDecoupledMsg(new TLBundleD(mergedParams), 4, 16)) - - // ======================= - // TL TX packets from System to the UCIe stack, push on the inward queue. - // The TX packets can be A request from manager node or D response from - // the client node. This needs to be arbitrated to be sent to partner die. - // ======================= - val txArbiter = Module(new Arbiter(new TLBundleAUnionD(tlParams), 2)) - val txATLPayload = Wire(new TLBundleAUnionD(tlParams)) - val txDTLPayload = Wire(new TLBundleAUnionD(tlParams)) - //val txTLPayload = Wire(new TLBundleAUnionD(tlParams)) - - val aHasData = manager_edge.hasData(manager_tl.a.bits) - val rx_fire = ucietop.io.TLplData_valid - val uciRxPayload = Wire(new UCIRawPayloadFormat(tlParams, protoParams)) // User-defined UCIe flit for streaming - val uciTxPayload = Wire(new UCIRawPayloadFormat(tlParams, protoParams)) // User-defined UCIe flit for streaming - - // defaults - uciRxPayload := 0.U.asTypeOf(new UCIRawPayloadFormat(tlParams, protoParams)) - uciTxPayload := 0.U.asTypeOf(new UCIRawPayloadFormat(tlParams, protoParams)) - txATLPayload := 0.U.asTypeOf(new TLBundleAUnionD(tlParams)) - txDTLPayload := 0.U.asTypeOf(new TLBundleAUnionD(tlParams)) - - /* - manager_tl.a.ready = (inward.io.enq.ready & ~ucietop.io.fdi_lpStallAck & - (ucietop.io.TLplStateStatus === PhyState.active)) - inward.io.enq.valid := manager_tl.a.fire - */ - - // A request to partner die logic - // enqueue on the A channel queue - manager_tl.a.ready := (inwardA.io.enq.ready & ~ucietop.io.fdi_lpStallAck & - (ucietop.io.TLplStateStatus === PhyState.active)) - inwardA.io.enq.valid := manager_tl.a.fire - inwardA.io.enq.bits <> manager_tl.a.bits - - val creditedMsgA = Module(new DecoupledtoCreditedMsg(new TLBundleA(mergedParams), 4, 16)) - inwardA.io.deq.ready := creditedMsgA.io.in.ready - creditedMsgA.io.in.valid := inwardA.io.deq.fire - creditedMsgA.io.in.bits := inwardA.io.deq.bits - creditedMsgA.io.credit.valid := rx_fire - creditedMsgA.io.credit.bits := uciRxPayload.cmd.tlACredit - - // D response to partner die's A request logic - client_tl.d.ready := (inwardD.io.enq.ready & ~ucietop.io.fdi_lpStallAck & - (ucietop.io.TLplStateStatus === PhyState.active)) - inwardD.io.enq.valid := client_tl.d.fire - inwardD.io.enq.bits <> client_tl.d.bits - - val creditedMsgD = Module(new DecoupledtoCreditedMsg(new TLBundleD(mergedParams), 4, 16)) - inwardD.io.deq.ready := creditedMsgD.io.in.ready - creditedMsgD.io.in.valid := inwardD.io.deq.fire - creditedMsgD.io.in.bits := inwardD.io.deq.bits - creditedMsgD.io.credit.valid := rx_fire - creditedMsgD.io.credit.bits := uciRxPayload.cmd.tlDCredit - - // Arbitrate the A and D channels from the credited msgs - creditedMsgA.io.out.ready := txArbiter.io.in(0).ready - txArbiter.io.in(0).valid := creditedMsgA.io.out.fire - //txArbiter.io.in(0).bits <> creditedMsgA.io.out.bits.asTypeOf(new TLBundleAUnionD(tlParams)) - txArbiter.io.in(0).bits.opcode := creditedMsgA.io.out.bits.opcode - txArbiter.io.in(0).bits.param := creditedMsgA.io.out.bits.param - txArbiter.io.in(0).bits.size := creditedMsgA.io.out.bits.size - txArbiter.io.in(0).bits.source := creditedMsgA.io.out.bits.source - txArbiter.io.in(0).bits.sink := 0.U - txArbiter.io.in(0).bits.address := creditedMsgA.io.out.bits.address - txArbiter.io.in(0).bits.mask := creditedMsgA.io.out.bits.mask - txArbiter.io.in(0).bits.data := creditedMsgA.io.out.bits.data - txArbiter.io.in(0).bits.msgType := UCIProtoMsgTypes.TLA - - creditedMsgD.io.out.ready := txArbiter.io.in(1).ready - txArbiter.io.in(1).valid := creditedMsgD.io.out.fire - //txArbiter.io.in(1).bits <> creditedMsgD.io.out.bits.asTypeOf(new TLBundleAUnionD(tlParams)) - txArbiter.io.in(1).bits.opcode := creditedMsgD.io.out.bits.opcode - txArbiter.io.in(1).bits.param := creditedMsgD.io.out.bits.param - txArbiter.io.in(1).bits.size := creditedMsgD.io.out.bits.size - txArbiter.io.in(1).bits.source := creditedMsgD.io.out.bits.source - txArbiter.io.in(1).bits.sink := creditedMsgD.io.out.bits.sink - txArbiter.io.in(1).bits.address := 0.U - txArbiter.io.in(1).bits.mask := 0.U - txArbiter.io.in(1).bits.data := creditedMsgD.io.out.bits.data - txArbiter.io.in(1).bits.msgType := UCIProtoMsgTypes.TLD - - // ============== Translated TL packet coming out of the outward queue to the system ======== - // dequeue the rx TL packets and orchestrate on the client/manager node - //val isRequest = outwardA.io.deq.bits.msgType - //val isResponse = outwardD.io.deq.bits.msgType - - outwardA.io.out.ready := client_tl.a.ready // if A request send to client node - outwardD.io.out.ready := manager_tl.d.ready // if D response send to manager node - - client_tl.a.bits <> outwardA.io.out.bits - client_tl.a.valid := outwardA.io.out.fire - - manager_tl.d.bits <> outwardD.io.out.bits - manager_tl.d.valid := outwardD.io.out.fire - - // ============ Below code should run on the UCIe clock? ============== - val checksum_reg = RegInit(0.U(64.W)) - checksum_reg := hammingEncoder.io.checksum - - val tx_pipe = Module(new Pipe(new UCIRawPayloadFormat(tlParams, protoParams), 1)) - tx_pipe.io.enq.bits := uciTxPayload - tx_pipe.io.enq.valid := txArbiter.io.out.fire - // Dequeue the TX TL packets and translate to UCIe flit - txArbiter.io.out.ready := ucietop.io.TLlpData_ready // if pl_trdy is asserted - // specs implies that these needs to be asserted at the same time - ucietop.io.TLlpData_valid := tx_pipe.io.deq.valid & (~ucietop.io.fdi_lpStallAck) - ucietop.io.TLlpData_irdy := tx_pipe.io.deq.valid & (~ucietop.io.fdi_lpStallAck) - ucietop.io.TLlpData_bits := Cat(tx_pipe.io.deq.bits.asUInt(511,64), checksum_reg.asUInt) // assign uciTXPayload to the FDI lp data signa - - val creditA = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLA) - val creditB = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLB) - val creditC = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLC) - val creditD = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLD) - val creditE = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLE) - - outwardA.io.credit.ready := tx_pipe.io.deq.valid && creditA - outwardD.io.credit.ready := tx_pipe.io.deq.valid && creditD - - val txACredit = WireDefault(0.U(protoParams.creditWidth.W)) - val txDCredit = WireDefault(0.U(protoParams.creditWidth.W)) - - when(outwardA.io.credit.valid){ - txACredit := outwardA.io.credit.bits - }.elsewhen(outwardD.io.credit.valid){ - txDCredit := outwardD.io.credit.bits - } - - // Translation based on the uciPayload formatting from outgoing TL packet - when(txArbiter.io.out.fire) { - // form the cmd header with TL message type - uciTxPayload.cmd.msgType := txArbiter.io.out.bits.msgType - uciTxPayload.cmd.hostID := 0.U - uciTxPayload.cmd.partnerID := 0.U - uciTxPayload.cmd.tlACredit := txACredit - uciTxPayload.cmd.tlBCredit := 0.U //& outwardB.io.credit.valid & creditB - uciTxPayload.cmd.tlCCredit := 0.U //& outwardC.io.credit.valid & creditC - uciTxPayload.cmd.tlDCredit := txDCredit - uciTxPayload.cmd.tlECredit := 0.U //& outwardE.io.credit.valid & creditE - uciTxPayload.cmd.reservedCmd := 0.U - // header 1 - uciTxPayload.header1.address := txArbiter.io.out.bits.address - // header 2 - uciTxPayload.header2.opcode := txArbiter.io.out.bits.opcode - uciTxPayload.header2.param := txArbiter.io.out.bits.param - uciTxPayload.header2.size := txArbiter.io.out.bits.size - uciTxPayload.header2.source := txArbiter.io.out.bits.source - uciTxPayload.header2.sink := txArbiter.io.out.bits.sink - uciTxPayload.header2.mask := txArbiter.io.out.bits.mask - uciTxPayload.header2.reservedh2 := 0.U - // uciTxPayload.header2.denied := txArbiter.io.out.bits.denied - // uciTxPayload.header2.corrupt := txArbiter.io.out.bits.corrupt - // data mapping - // TODO: replace with FP coding - uciTxPayload.data(0) := txArbiter.io.out.bits.data(63,0) - uciTxPayload.data(1) := txArbiter.io.out.bits.data(127,64) - uciTxPayload.data(2) := txArbiter.io.out.bits.data(191,128) - uciTxPayload.data(3) := txArbiter.io.out.bits.data(255,192) - // TODO: add ECC/checksum functionality, for now tieing to 0 - hammingEncoder.io.data := uciTxPayload.asUInt(511,64) - uciTxPayload.ecc := 0.U - } - - - // ======================= - // TL RX packets coming from the UCIe stack to the System, push on the outward queue - // ======================= - val rxTLPayload = Wire(new TLBundleAUnionD(tlParams)) - rxTLPayload := 0.U.asTypeOf(new TLBundleAUnionD(tlParams)) - // ucietop.io.fdi_lpData.irdy := outward.io.enq.ready - // map the uciRxPayload and the plData based on the uciPayload formatting - // map the uciRxPayload to the rxTLPayload TLBundle - when(rx_fire) { - // ucie cmd - uciRxPayload.cmd := ucietop.io.TLplData_bits(511, 448).asTypeOf(new UCICmdFormat(protoParams)) - // ucie header 1 - uciRxPayload.header1 := ucietop.io.TLplData_bits(447,384).asTypeOf(new UCIHeader1Format(tlParams)) - // ucie header 2 - uciRxPayload.header2 := ucietop.io.TLplData_bits(383, 320).asTypeOf(new UCIHeader2Format(tlParams)) - // ucie data payload - uciRxPayload.data(3) := ucietop.io.TLplData_bits(319,256) - uciRxPayload.data(2) := ucietop.io.TLplData_bits(255,192) - uciRxPayload.data(1) := ucietop.io.TLplData_bits(191,128) - uciRxPayload.data(0) := ucietop.io.TLplData_bits(127,64) - // ucie ecc - uciRxPayload.ecc := ucietop.io.TLplData_bits(63,0) - hammingDecoder.io.data := ucietop.io.TLplData_bits(511,64) - hammingDecoder.io.checksum := ucietop.io.TLplData_bits(63,0) - - // map the uciRxPayload to the rxTLPayload - rxTLPayload.address := uciRxPayload.header1.address - rxTLPayload.opcode := uciRxPayload.header2.opcode - rxTLPayload.param := uciRxPayload.header2.param - rxTLPayload.size := uciRxPayload.header2.size - rxTLPayload.source := uciRxPayload.header2.source - rxTLPayload.sink := uciRxPayload.header2.sink - rxTLPayload.mask := uciRxPayload.header2.mask - // rxTLPayload.denied := uciRxPayload.header2.denied - // rxTLPayload.corrupt := uciRxPayload.header2.corrupt - rxTLPayload.data := Cat(uciRxPayload.data(0), uciRxPayload.data(1), uciRxPayload.data(2), uciRxPayload.data(3)) - rxTLPayload.msgType := uciRxPayload.cmd.msgType - } - fault := ~(hammingDecoder.io.matches) - - outwardA.io.in.bits.address := rxTLPayload.address - outwardA.io.in.bits.opcode := rxTLPayload.opcode - outwardA.io.in.bits.param := rxTLPayload.param - outwardA.io.in.bits.size := rxTLPayload.size - outwardA.io.in.bits.source := rxTLPayload.source - outwardA.io.in.bits.mask := rxTLPayload.mask - outwardA.io.in.bits.data := rxTLPayload.data - outwardA.io.in.bits.corrupt := false.B - outwardA.io.in.valid := false.B - - outwardD.io.in.bits.opcode := rxTLPayload.opcode - outwardD.io.in.bits.param := rxTLPayload.param - outwardD.io.in.bits.size := rxTLPayload.size - outwardD.io.in.bits.source := rxTLPayload.source - outwardD.io.in.bits.sink := rxTLPayload.sink - outwardD.io.in.bits.data := rxTLPayload.data - outwardD.io.in.bits.denied := false.B - outwardD.io.in.bits.corrupt := false.B - outwardD.io.in.valid := false.B - - // Queue the translated RX TL packet to send to the system - when(rx_fire) { - when(uciRxPayload.cmd.msgType === UCIProtoMsgTypes.TLA && outwardA.io.in.ready) { - outwardA.io.in.valid := true.B - }.elsewhen(uciRxPayload.cmd.msgType === UCIProtoMsgTypes.TLD && outwardD.io.in.ready ) { - outwardD.io.in.valid := true.B + class UCITLFrontImp extends Impl { + val io = IO(new UcieDigitalTopIO(afeParams.mbLanes, afeParams.STANDALONE)) + // new Bundle { + //  FDI interface for testing purposes only + // val fdi = new Fdi(fdiParams) + // IOs for connecting to the AFE + // val mbAfe = new MainbandAfeIo(afeParams) + // val mbAfe_tx = Output(new MainbandIo(afeParams.mbLanes)) + // val mbAfe_rx = Input (new MainbandIo(afeParams.mbLanes)) + // val sbTxIo = Output(new SidebandIo) + // val sbRxIo = Input(new SidebandIo) + // }) + + withClockAndReset(clock, reset) { + + val fault = RegInit(false.B) // if fault in ecc code + + // Instantiate the agnostic protocol layer + // val protocol = Module(new ProtocolLayer(fdiParams)) + val ucietop = Module( + new UCITop( + fdiParams, + rdiParams, + sbParams, + linkTrainingParams, + afeParams, + laneAsyncQueueParams, + ), + ) + // io.fdi <> ucietop.io.fdi + ucietop.io.fault := fault + + if (afeParams.STANDALONE) { + io.mbAfe_tx.get <> ucietop.io.mbAfe_tx.get + io.mbAfe_rx.get <> ucietop.io.mbAfe_rx.get + } else { + io.phyAfe.get <> ucietop.io.phyAfe.get + io.train.get <> ucietop.io.train.get + } + io.txSbAfe <> ucietop.io.sbTxIO + io.rxSbAfe <> ucietop.io.sbRxIO + // dontTouch(topIO.out(0)._1) + // topIO.out(0)._1.mbAfe_tx <> ucietop.io.mbAfe_tx + // topIO.out(0)._1.mbAfe_rx <> ucietop.io.mbAfe_rx + // topIO.out(0)._1.rxSbAfe <> ucietop.io.sbRxIO + // topIO.out(0)._1.txSbAfe <> ucietop.io.sbTxIO + + // Hamming encode and decode + val hammingEncoder = Module(new HammingEncode(protoParams)) + val hammingDecoder = Module(new HammingDecode(protoParams)) + // Defaults + hammingEncoder.io.data := 0.U + hammingDecoder.io.data := 0.U + hammingDecoder.io.checksum := 0.U + + // Sideband node for protocol layer + val protocol_sb_node = + Module(new SidebandNode((new SidebandParams), fdiParams)) + + protocol_sb_node.io.outer.rx.bits := ucietop.io.fdi_lpConfig.bits + protocol_sb_node.io.outer.rx.valid := ucietop.io.fdi_lpConfig.valid + ucietop.io.fdi_lpConfigCredit := protocol_sb_node.io.outer.rx.credit + + ucietop.io.fdi_plConfig.bits := protocol_sb_node.io.outer.tx.bits + ucietop.io.fdi_plConfig.valid := protocol_sb_node.io.outer.tx.valid + protocol_sb_node.io.outer.tx.credit := ucietop.io.fdi_plConfigCredit + + protocol_sb_node.io.inner.layer_to_node.bits := Cat( + regNode.module.io.sb_csrs.sideband_mailbox_sw_to_node_data_high, + regNode.module.io.sb_csrs.sideband_mailbox_sw_to_node_data_low, + regNode.module.io.sb_csrs.sidebank_mailbox_sw_to_node_index_high, + regNode.module.io.sb_csrs.sideband_mailbox_sw_to_node_index_low, + ) + protocol_sb_node.io.inner.layer_to_node.valid := regNode.module.io.sb_csrs.sideband_mailbox_sw_valid + + regNode.module.io.sb_csrs.sideband_mailbox_index_low := protocol_sb_node.io.inner.node_to_layer + .bits(31, 0) + regNode.module.io.sb_csrs.sideband_mailbox_index_high := protocol_sb_node.io.inner.node_to_layer + .bits(63, 32) + regNode.module.io.sb_csrs.sideband_mailbox_data_low := protocol_sb_node.io.inner.node_to_layer + .bits(95, 64) + regNode.module.io.sb_csrs.sideband_mailbox_data_high := protocol_sb_node.io.inner.node_to_layer + .bits(127, 96) + protocol_sb_node.io.inner.node_to_layer.ready := regNode.module.io.sb_csrs.sideband_mailbox_ready + regNode.module.io.sb_csrs.sideband_mailbox_valid := protocol_sb_node.io.inner.node_to_layer.valid + + val tlBundleParams = new TLBundleParameters( + addressBits = tlParams.addressWidth, + dataBits = tlParams.dataWidth, + sourceBits = tlParams.sourceIDWidth, + sinkBits = tlParams.sinkIDWidth, + sizeBits = tlParams.sizeWidth, + echoFields = Nil, + requestFields = Nil, + responseFields = Nil, + hasBCE = false, + ) + + val client_tl = clientNode.out(0)._1 + val client_edge = clientNode.out(0)._2 + val manager_tl = managerNode.in(0)._1 + val manager_edge = managerNode.in(0)._2 + + val clientParams = client_edge.bundle + val managerParams = manager_edge.bundle + val mergedParams = clientParams.union(managerParams).union(tlBundleParams) + + require( + mergedParams.echoFields.isEmpty, + "UCIe does not support TileLink with echo fields", + ) + require( + mergedParams.requestFields.isEmpty, + "UCIe does not support TileLink with request fields", + ) + require( + mergedParams.responseFields.isEmpty, + "UCIe does not support TileLink with response fields", + ) + require( + mergedParams == tlBundleParams, + s"UCIe is misconfigured, the combined inwards/outwards parameters cannot be serialized using the provided bundle params\n$mergedParams > $tlBundleParams", + ) + + val inwardA = Module( + new Queue((new TLBundleA(mergedParams)), 16, pipe = true, flow = true), + ) + val inwardD = Module( + new Queue((new TLBundleD(mergedParams)), 16, pipe = true, flow = true), + ) + + val outwardA = + Module(new CreditedToDecoupledMsg(new TLBundleA(mergedParams), 4, 16)) + val outwardD = + Module(new CreditedToDecoupledMsg(new TLBundleD(mergedParams), 4, 16)) + + // ======================= + // TL TX packets from System to the UCIe stack, push on the inward queue. + // The TX packets can be A request from manager node or D response from + // the client node. This needs to be arbitrated to be sent to partner die. + // ======================= + val txArbiter = Module(new Arbiter(new TLBundleAUnionD(tlParams), 2)) + val txATLPayload = Wire(new TLBundleAUnionD(tlParams)) + val txDTLPayload = Wire(new TLBundleAUnionD(tlParams)) + // val txTLPayload = Wire(new TLBundleAUnionD(tlParams)) + + val aHasData = manager_edge.hasData(manager_tl.a.bits) + val rx_fire = ucietop.io.TLplData_valid + val uciRxPayload = Wire( + new UCIRawPayloadFormat(tlParams, protoParams), + ) // User-defined UCIe flit for streaming + val uciTxPayload = Wire( + new UCIRawPayloadFormat(tlParams, protoParams), + ) // User-defined UCIe flit for streaming + + // defaults + uciRxPayload := 0.U.asTypeOf( + new UCIRawPayloadFormat(tlParams, protoParams), + ) + uciTxPayload := 0.U.asTypeOf( + new UCIRawPayloadFormat(tlParams, protoParams), + ) + txATLPayload := 0.U.asTypeOf(new TLBundleAUnionD(tlParams)) + txDTLPayload := 0.U.asTypeOf(new TLBundleAUnionD(tlParams)) + + /* manager_tl.a.ready = (inward.io.enq.ready & ~ucietop.io.fdi_lpStallAck + * & (ucietop.io.TLplStateStatus === PhyState.active)) inward.io.enq.valid + * := manager_tl.a.fire */ + + // A request to partner die logic + // enqueue on the A channel queue + manager_tl.a.ready := (inwardA.io.enq.ready & ~ucietop.io.fdi_lpStallAck & + (ucietop.io.TLplStateStatus === PhyState.active)) + inwardA.io.enq.valid := manager_tl.a.fire + inwardA.io.enq.bits <> manager_tl.a.bits + + val creditedMsgA = + Module(new DecoupledtoCreditedMsg(new TLBundleA(mergedParams), 4, 16)) + inwardA.io.deq.ready := creditedMsgA.io.in.ready + creditedMsgA.io.in.valid := inwardA.io.deq.fire + creditedMsgA.io.in.bits := inwardA.io.deq.bits + creditedMsgA.io.credit.valid := rx_fire + creditedMsgA.io.credit.bits := uciRxPayload.cmd.tlACredit + + // D response to partner die's A request logic + client_tl.d.ready := (inwardD.io.enq.ready & ~ucietop.io.fdi_lpStallAck & + (ucietop.io.TLplStateStatus === PhyState.active)) + inwardD.io.enq.valid := client_tl.d.fire + inwardD.io.enq.bits <> client_tl.d.bits + + val creditedMsgD = + Module(new DecoupledtoCreditedMsg(new TLBundleD(mergedParams), 4, 16)) + inwardD.io.deq.ready := creditedMsgD.io.in.ready + creditedMsgD.io.in.valid := inwardD.io.deq.fire + creditedMsgD.io.in.bits := inwardD.io.deq.bits + creditedMsgD.io.credit.valid := rx_fire + creditedMsgD.io.credit.bits := uciRxPayload.cmd.tlDCredit + + // Arbitrate the A and D channels from the credited msgs + creditedMsgA.io.out.ready := txArbiter.io.in(0).ready + txArbiter.io.in(0).valid := creditedMsgA.io.out.fire + /* txArbiter.io.in(0).bits <> creditedMsgA.io.out.bits.asTypeOf(new + * TLBundleAUnionD(tlParams)) */ + txArbiter.io.in(0).bits.opcode := creditedMsgA.io.out.bits.opcode + txArbiter.io.in(0).bits.param := creditedMsgA.io.out.bits.param + txArbiter.io.in(0).bits.size := creditedMsgA.io.out.bits.size + txArbiter.io.in(0).bits.source := creditedMsgA.io.out.bits.source + txArbiter.io.in(0).bits.sink := 0.U + txArbiter.io.in(0).bits.address := creditedMsgA.io.out.bits.address + txArbiter.io.in(0).bits.mask := creditedMsgA.io.out.bits.mask + txArbiter.io.in(0).bits.data := creditedMsgA.io.out.bits.data + txArbiter.io.in(0).bits.msgType := UCIProtoMsgTypes.TLA + + creditedMsgD.io.out.ready := txArbiter.io.in(1).ready + txArbiter.io.in(1).valid := creditedMsgD.io.out.fire + /* txArbiter.io.in(1).bits <> creditedMsgD.io.out.bits.asTypeOf(new + * TLBundleAUnionD(tlParams)) */ + txArbiter.io.in(1).bits.opcode := creditedMsgD.io.out.bits.opcode + txArbiter.io.in(1).bits.param := creditedMsgD.io.out.bits.param + txArbiter.io.in(1).bits.size := creditedMsgD.io.out.bits.size + txArbiter.io.in(1).bits.source := creditedMsgD.io.out.bits.source + txArbiter.io.in(1).bits.sink := creditedMsgD.io.out.bits.sink + txArbiter.io.in(1).bits.address := 0.U + txArbiter.io.in(1).bits.mask := 0.U + txArbiter.io.in(1).bits.data := creditedMsgD.io.out.bits.data + txArbiter.io.in(1).bits.msgType := UCIProtoMsgTypes.TLD + + /* ============== Translated TL packet coming out of the outward queue to + * the system ======== */ + // dequeue the rx TL packets and orchestrate on the client/manager node + // val isRequest = outwardA.io.deq.bits.msgType + // val isResponse = outwardD.io.deq.bits.msgType + + outwardA.io.out.ready := client_tl.a.ready // if A request send to client node + outwardD.io.out.ready := manager_tl.d.ready // if D response send to manager node + + client_tl.a.bits <> outwardA.io.out.bits + client_tl.a.valid := outwardA.io.out.fire + + manager_tl.d.bits <> outwardD.io.out.bits + manager_tl.d.valid := outwardD.io.out.fire + + // ============ Below code should run on the UCIe clock? ============== + val checksum_reg = RegInit(0.U(64.W)) + checksum_reg := hammingEncoder.io.checksum + + val tx_pipe = + Module(new Queue(new UCIRawPayloadFormat(tlParams, protoParams), 1)) + tx_pipe.io.enq.bits := uciTxPayload + tx_pipe.io.enq.valid := txArbiter.io.out.fire + // Dequeue the TX TL packets and translate to UCIe flit + txArbiter.io.out.ready := tx_pipe.io.enq.ready + tx_pipe.io.deq.ready := ucietop.io.TLlpData_ready // if pl_trdy is asserted + // specs implies that these needs to be asserted at the same time + ucietop.io.TLlpData_valid := tx_pipe.io.deq.fire & (~ucietop.io.fdi_lpStallAck) + ucietop.io.TLlpData_irdy := tx_pipe.io.deq.fire & (~ucietop.io.fdi_lpStallAck) + ucietop.io.TLlpData_bits := Cat( + tx_pipe.io.deq.bits.asUInt(511, 64), + checksum_reg.asUInt, + ) // assign uciTXPayload to the FDI lp data signa + + val creditA = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLA) + val creditB = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLB) + val creditC = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLC) + val creditD = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLD) + val creditE = (txArbiter.io.out.bits.msgType === UCIProtoMsgTypes.TLE) + + outwardA.io.credit.ready := tx_pipe.io.deq.fire && creditA + outwardD.io.credit.ready := tx_pipe.io.deq.fire && creditD + + val txACredit = WireDefault(0.U(protoParams.creditWidth.W)) + val txDCredit = WireDefault(0.U(protoParams.creditWidth.W)) + + when(outwardA.io.credit.valid) { + txACredit := outwardA.io.credit.bits + }.elsewhen(outwardD.io.credit.valid) { + txDCredit := outwardD.io.credit.bits + } + + // Translation based on the uciPayload formatting from outgoing TL packet + when(txArbiter.io.out.fire) { + // form the cmd header with TL message type + uciTxPayload.cmd.msgType := txArbiter.io.out.bits.msgType + uciTxPayload.cmd.hostID := 0.U + uciTxPayload.cmd.partnerID := 0.U + uciTxPayload.cmd.tlACredit := txACredit + uciTxPayload.cmd.tlBCredit := 0.U // & outwardB.io.credit.valid & creditB + uciTxPayload.cmd.tlCCredit := 0.U // & outwardC.io.credit.valid & creditC + uciTxPayload.cmd.tlDCredit := txDCredit + uciTxPayload.cmd.tlECredit := 0.U // & outwardE.io.credit.valid & creditE + uciTxPayload.cmd.reservedCmd := 0.U + // header 1 + uciTxPayload.header1.address := txArbiter.io.out.bits.address + // header 2 + uciTxPayload.header2.opcode := txArbiter.io.out.bits.opcode + uciTxPayload.header2.param := txArbiter.io.out.bits.param + uciTxPayload.header2.size := txArbiter.io.out.bits.size + uciTxPayload.header2.source := txArbiter.io.out.bits.source + uciTxPayload.header2.sink := txArbiter.io.out.bits.sink + uciTxPayload.header2.mask := txArbiter.io.out.bits.mask + uciTxPayload.header2.reservedh2 := 0.U + // uciTxPayload.header2.denied := txArbiter.io.out.bits.denied + // uciTxPayload.header2.corrupt := txArbiter.io.out.bits.corrupt + // data mapping + // TODO: replace with FP coding + uciTxPayload.data(0) := txArbiter.io.out.bits.data(63, 0) + uciTxPayload.data(1) := txArbiter.io.out.bits.data(127, 64) + uciTxPayload.data(2) := txArbiter.io.out.bits.data(191, 128) + uciTxPayload.data(3) := txArbiter.io.out.bits.data(255, 192) + // TODO: add ECC/checksum functionality, for now tieing to 0 + hammingEncoder.io.data := uciTxPayload.asUInt(511, 64) + uciTxPayload.ecc := 0.U + } + + // ======================= + /* TL RX packets coming from the UCIe stack to the System, push on the + * outward queue */ + // ======================= + val rxTLPayload = Wire(new TLBundleAUnionD(tlParams)) + rxTLPayload := 0.U.asTypeOf(new TLBundleAUnionD(tlParams)) + // ucietop.io.fdi_lpData.irdy := outward.io.enq.ready + // map the uciRxPayload and the plData based on the uciPayload formatting + // map the uciRxPayload to the rxTLPayload TLBundle + when(rx_fire) { + // ucie cmd + uciRxPayload.cmd := ucietop.io + .TLplData_bits(511, 448) + .asTypeOf(new UCICmdFormat(protoParams)) + // ucie header 1 + uciRxPayload.header1 := ucietop.io + .TLplData_bits(447, 384) + .asTypeOf(new UCIHeader1Format(tlParams)) + // ucie header 2 + uciRxPayload.header2 := ucietop.io + .TLplData_bits(383, 320) + .asTypeOf(new UCIHeader2Format(tlParams)) + // ucie data payload + uciRxPayload.data(3) := ucietop.io.TLplData_bits(319, 256) + uciRxPayload.data(2) := ucietop.io.TLplData_bits(255, 192) + uciRxPayload.data(1) := ucietop.io.TLplData_bits(191, 128) + uciRxPayload.data(0) := ucietop.io.TLplData_bits(127, 64) + // ucie ecc + uciRxPayload.ecc := ucietop.io.TLplData_bits(63, 0) + hammingDecoder.io.data := ucietop.io.TLplData_bits(511, 64) + hammingDecoder.io.checksum := ucietop.io.TLplData_bits(63, 0) + + // map the uciRxPayload to the rxTLPayload + rxTLPayload.address := uciRxPayload.header1.address + rxTLPayload.opcode := uciRxPayload.header2.opcode + rxTLPayload.param := uciRxPayload.header2.param + rxTLPayload.size := uciRxPayload.header2.size + rxTLPayload.source := uciRxPayload.header2.source + rxTLPayload.sink := uciRxPayload.header2.sink + rxTLPayload.mask := uciRxPayload.header2.mask + // rxTLPayload.denied := uciRxPayload.header2.denied + // rxTLPayload.corrupt := uciRxPayload.header2.corrupt + rxTLPayload.data := Cat( + uciRxPayload.data(3), + uciRxPayload.data(2), + uciRxPayload.data(1), + uciRxPayload.data(0), + ) + rxTLPayload.msgType := uciRxPayload.cmd.msgType + } + fault := ~(hammingDecoder.io.matches) + + outwardA.io.in.bits.address := rxTLPayload.address + outwardA.io.in.bits.opcode := rxTLPayload.opcode + outwardA.io.in.bits.param := rxTLPayload.param + outwardA.io.in.bits.size := rxTLPayload.size + outwardA.io.in.bits.source := rxTLPayload.source + outwardA.io.in.bits.mask := rxTLPayload.mask + outwardA.io.in.bits.data := rxTLPayload.data + outwardA.io.in.bits.corrupt := false.B + outwardA.io.in.valid := false.B + + outwardD.io.in.bits.opcode := rxTLPayload.opcode + outwardD.io.in.bits.param := rxTLPayload.param + outwardD.io.in.bits.size := rxTLPayload.size + outwardD.io.in.bits.source := rxTLPayload.source + outwardD.io.in.bits.sink := rxTLPayload.sink + outwardD.io.in.bits.data := rxTLPayload.data + outwardD.io.in.bits.denied := false.B + outwardD.io.in.bits.corrupt := false.B + outwardD.io.in.valid := false.B + + // Queue the translated RX TL packet to send to the system + when(rx_fire) { + when( + uciRxPayload.cmd.msgType === UCIProtoMsgTypes.TLA && outwardA.io.in.ready, + ) { + outwardA.io.in.valid := true.B + }.elsewhen( + uciRxPayload.cmd.msgType === UCIProtoMsgTypes.TLD && outwardD.io.in.ready, + ) { + outwardD.io.in.valid := true.B + } + } + // when the RX queues are ready to get data + val TLready_to_rcv = outwardA.io.in.ready || outwardD.io.in.ready + ucietop.io.TLready_to_rcv := TLready_to_rcv + + /* soft resets: can be reset or flush and reset, in flush and reset, the + * packets are */ + // sent out before triggering reset + ucietop.io.soft_reset := (regNode.module.io.d2d_csrs.d2d_state_can_reset | + regNode.module.io.d2d_csrs.d2d_flush_and_reset) } } - // when the RX queues are ready to get data - val TLready_to_rcv = outwardA.io.in.ready || outwardD.io.in.ready - ucietop.io.TLready_to_rcv := TLready_to_rcv - - // soft resets: can be reset or flush and reset, in flush and reset, the packets are - // sent out before triggering reset - ucietop.io.soft_reset := (regNode.module.io.d2d_csrs.d2d_state_can_reset | - regNode.module.io.d2d_csrs.d2d_flush_and_reset) -}}}} \ No newline at end of file +} diff --git a/src/main/scala/utils/ClockMux.scala b/src/main/scala/utils/ClockMux.scala new file mode 100644 index 00000000..ce2afc67 --- /dev/null +++ b/src/main/scala/utils/ClockMux.scala @@ -0,0 +1,16 @@ +package edu.berkeley.cs.ucie.digital +package utils + +import chisel3._ +import chisel3.util._ + +class ClockMux2 extends BlackBox with HasBlackBoxResource { + + val io = IO(new Bundle { + val clocksIn = Input(Vec(2, Clock())) + val sel = Input(Bool()) + val clockOut = Output(Clock()) + }) + + addResource("/vsrc/ClockSelector.v") +} diff --git a/src/main/scala/utils/ConsistentFibonacciLFSR.scala b/src/main/scala/utils/ConsistentFibonacciLFSR.scala new file mode 100644 index 00000000..e2eb8a7b --- /dev/null +++ b/src/main/scala/utils/ConsistentFibonacciLFSR.scala @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: Apache-2.0 + +// THIS IMPLEMENTATION IS RIPPED FROM CHISEL BUT DOES NOT USE SET SO THE GENERATED VERILOG IS CONSISTENT +package chisel3.util.random + +import chisel3._ + + +/** Fibonacci Linear Feedback Shift Register (LFSR) generator. + * + * A Fibonacci LFSR can be generated by defining a width and a set of tap points (corresponding to a polynomial). An + * optional initial seed and a reduction operation ([[XOR]], the default, or [[XNOR]]) can be used to augment the + * generated hardware. The resulting hardware has support for a run-time programmable seed (via [[PRNGIO.seed]]) and + * conditional increment (via [[PRNGIO.increment]]). + * + * $seedExplanation + * + * In the example below, a 4-bit Fibonacci LFSR is constructed. Tap points are defined as four and three (using LFSR + * convention of indexing from one). This results in the hardware configuration shown in the diagram. + * + * {{{ + * val lfsr4 = Module(new FibonacciLFSR(4, Set(4, 3)) + * // +---+ + * // +-------------->|XOR|-------------------------------------------------------+ + * // | +---+ | + * // | +-------+ ^ +-------+ +-------+ +-------+ | + * // | | | | | | | | | | | + * // +---+ x^4 |<----+-----| x^3 |<----------| x^2 |<----------| x^1 |<--+ + * // | | | | | | | | + * // +-------+ +-------+ +-------+ +-------+ + * }}} + * + * If you require a maximal period Fibonacci LFSR of a specific width, you can use [[MaxPeriodFibonacciLFSR]]. If you + * only require a pseudorandom [[UInt]] you can use the [[FibonacciLFSR$ FibonacciLFSR companion + * object]]. + * @see [[https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Fibonacci_LFSRs]] + * $paramWidth + * $paramTaps + * $paramSeed + * $paramReduction + * $paramStep + * $paramUpdateSeed + */ +class ConsistentFibonacciLFSR( + width: Int, + taps: Seq[Int], + seed: Option[BigInt] = Some(1), + val reduction: chisel3.util.random.LFSRReduce = chisel3.util.random.XOR, + step: Int = 1, + updateSeed: Boolean = false +) extends chisel3.util.random.PRNG(width, seed, step, updateSeed) + with chisel3.util.random.LFSR { + + def delta(s: Seq[Bool]): Seq[Bool] = taps.sorted.map { case i => s(i - 1) }.reduce(reduction) +: s.dropRight(1) + + +} + +/** A maximal period Fibonacci Linear Feedback Shift Register (LFSR) generator. The maximal period taps are sourced from + * [[LFSR.tapsMaxPeriod LFSR.tapsMaxPeriod]]. + * {{{ + * val lfsr8 = Module(new MaxPeriodFibonacciLFSR(8)) + * }}} + * $paramWidth + * $paramSeed + * $paramReduction + */ +class ConsistentMaxPeriodFibonacciLFSR(width: Int, seed: Option[BigInt] = Some(1), reduction: chisel3.util.random.LFSRReduce = chisel3.util.random.XOR) + extends ConsistentFibonacciLFSR(width, chisel3.util.random.LFSR.tapsMaxPeriod.getOrElse(width, chisel3.util.random.LFSR.badWidth(width)).head.toSeq, seed, reduction) + +/** Utility for generating a pseudorandom [[UInt]] from a [[FibonacciLFSR]]. + * + * For example, to generate a pseudorandom 8-bit [[UInt]] that changes every cycle, you can use: + * {{{ + * val pseudoRandomNumber = FibonacciLFSR.maxPeriod(8) + * }}} + * + * @define paramWidth @param width of pseudorandom output + * @define paramTaps @param taps a set of tap points to use when constructing the LFSR + * @define paramIncrement @param increment when asserted, a new random value will be generated + * @define paramSeed @param seed an initial value for internal LFSR state + * @define paramReduction @param reduction the reduction operation (either [[XOR]] or + * [[XNOR]]) + */ +object ConsistentFibonacciLFSR { + + /** Return a pseudorandom [[UInt]] generated from a [[FibonacciLFSR]]. + * $paramWidth + * $paramTaps + * $paramIncrement + * $paramSeed + * $paramReduction + */ + def apply( + width: Int, + taps: Seq[Int], + increment: Bool = true.B, + seed: Option[BigInt] = Some(1), + reduction: chisel3.util.random.LFSRReduce = chisel3.util.random.XOR + ): UInt = chisel3.util.random.PRNG(new ConsistentFibonacciLFSR(width, taps, seed, reduction), increment) + + /** Return a pseudorandom [[UInt]] generated using a maximal period [[FibonacciLFSR]] + * $paramWidth + * $paramIncrement + * $paramSeed + * $paramReduction + */ + def maxPeriod( + width: Int, + increment: Bool = true.B, + seed: Option[BigInt] = Some(1), + reduction: chisel3.util.random.LFSRReduce = chisel3.util.random.XOR + ): UInt = chisel3.util.random.PRNG(new ConsistentMaxPeriodFibonacciLFSR(width, seed, reduction), increment) + +} \ No newline at end of file diff --git a/src/test/scala/Scrambler.scala b/src/test/scala/Scrambler.scala deleted file mode 100644 index 80a4d145..00000000 --- a/src/test/scala/Scrambler.scala +++ /dev/null @@ -1,40 +0,0 @@ -package edu.berkeley.cs.ucie.digital -package interfaces - -import chisel3._ -import chiseltest._ -import org.scalatest.funspec.AnyFunSpec - -class ScramblerTest extends AnyFunSpec with ChiselScalatestTester { - - describe("Scrambler") { - it("4 lane scrambler test") { - test(new UCIeScrambler(new AfeParams(), 16, 4)) { c => - c.reset.poke(true.B) - c.clock.step() - c.clock.step() - c.reset.poke(false.B) - c.clock.step() - c.io.valid.poke(true.B) - - c.io.data_in(0).poke(1.U(16.W)) - c.io.data_in(1).poke(1012.U(16.W)) - c.io.data_in(2).poke(823.U(16.W)) - c.io.data_in(3).poke(134.U(16.W)) - c.io.data_out(0).expect(49085.U(16.W)) - c.io.data_out(1).expect(1103.U(16.W)) - c.io.data_out(2).expect(50263.U(16.W)) - c.io.data_out(3).expect(49245.U(16.W)) - c.clock.step() - c.io.data_in(0).poke(203.U(16.W)) - c.io.data_in(1).poke(176.U(16.W)) - c.io.data_in(2).poke(21.U(16.W)) - c.io.data_in(3).poke(5847.U(16.W)) - c.io.data_out(0).expect(65321.U(16.W)) - c.io.data_out(1).expect(56489.U(16.W)) - c.io.data_out(2).expect(11245.U(16.W)) - c.io.data_out(3).expect(57654.U(16.W)) - } - } - } -} diff --git a/src/test/scala/e2e/AfeLoopback.scala b/src/test/scala/e2e/AfeLoopback.scala index 1f9c3214..7c3ff13e 100644 --- a/src/test/scala/e2e/AfeLoopback.scala +++ b/src/test/scala/e2e/AfeLoopback.scala @@ -5,7 +5,12 @@ import chisel3._ import chisel3.util._ import interfaces._ import org.chipsalliance.cde.config.{Field, Parameters, Config} -import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public} +import chisel3.experimental.hierarchy.{ + Definition, + Instance, + instantiable, + public, +} import freechips.rocketchip.diplomacy._ import freechips.rocketchip.tilelink._ import freechips.rocketchip.devices.tilelink.{TLTestRAM} @@ -13,31 +18,55 @@ import protocol._ // @instantiable class AfeLoopback(val afeParams: AfeParams) extends Module { - val io = IO(new Bundle { - // val finished = Output(Bool()) - val mbAfe = Flipped(new MainbandAfeIo(afeParams)) - val sbAfe = Flipped(new SidebandAfeIo(afeParams)) - }) + val io = IO(new Bundle { + // val finished = Output(Bool()) + // val mbAfe = Flipped(new MainbandAfeIo(afeParams)) + val mbAfe_tx = Input(new MainbandIo(afeParams.mbLanes)) + val mbAfe_rx = Output(new MainbandIo(afeParams.mbLanes)) + val sbAfe_tx = Input(new SidebandIo) + val sbAfe_rx = Output(new SidebandIo) + }) - val latency = 2 - val delayerMb = Module(new Pipe(chiselTypeOf(io.mbAfe.txData.bits), latency)) - val delayerSb = Module(new Pipe(chiselTypeOf(io.sbAfe.txData), latency)) - - delayerMb.io.enq.valid := io.mbAfe.txData.valid - delayerMb.io.enq.bits := io.mbAfe.txData.bits - io.mbAfe.rxData.bits := delayerMb.io.deq.bits - io.mbAfe.rxData.valid := delayerMb.io.deq.valid - io.mbAfe.txData.ready := true.B - io.mbAfe.fifoParams.clk := clock - io.mbAfe.fifoParams.reset := reset - io.mbAfe.pllLock := true.B + val latency = 100 + /* val delayerMb = Module(new Pipe(chiselTypeOf(io.mbAfe.txData.bits), + * latency)) */ + val delayerMbTx = Module(new Pipe(chiselTypeOf(io.mbAfe_tx.data), latency)) + val delayerSb = Module(new Pipe(chiselTypeOf(io.sbAfe_tx.data), latency)) + val delayerSb_clock = Module( + new Pipe(chiselTypeOf(io.sbAfe_tx.clk), latency), + ) + val delayerMbTx_clockn = Module( + new Pipe(chiselTypeOf(io.mbAfe_tx.clkn.asBool), latency), + ) + val delayerMbTx_clockp = Module( + new Pipe(chiselTypeOf(io.mbAfe_tx.clkp.asBool), latency), + ) - delayerSb.io.enq.valid := true.B //io.sbAfe.txData.valid - delayerSb.io.enq.bits := io.sbAfe.txData - io.sbAfe.rxData := delayerSb.io.deq.bits - //io.sbAfe.rxData.valid := delayerSb.io.deq.valid - io.sbAfe.rxClock := io.sbAfe.txClock - io.sbAfe.fifoParams.clk := clock - io.sbAfe.fifoParams.reset := reset - io.sbAfe.pllLock := true.B - } \ No newline at end of file + /** TODO: ansa fix delayed signals -- not delayed */ + delayerMbTx.io.enq.valid := io.mbAfe_tx.valid + delayerMbTx.io.enq.bits := io.mbAfe_tx.data + io.mbAfe_rx.data := delayerMbTx.io.deq.bits + io.mbAfe_rx.valid := delayerMbTx.io.deq.valid + delayerMbTx_clockn.io.enq.valid := true.B + delayerMbTx_clockn.io.enq.bits := io.mbAfe_tx.clkn.asBool + delayerMbTx_clockp.io.enq.valid := true.B + delayerMbTx_clockp.io.enq.bits := io.mbAfe_tx.clkp.asBool + + io.mbAfe_rx.clkn := io.mbAfe_tx.clkn + io.mbAfe_rx.clkp := io.mbAfe_tx.clkp + io.mbAfe_rx.track := false.B + + delayerSb.io.enq.valid := true.B // io.sbAfe.txData.valid + delayerSb.io.enq.bits := io.sbAfe_tx.data + delayerSb_clock.io.enq.valid := true.B + delayerSb_clock.io.enq.bits := io.sbAfe_tx.clk + + io.sbAfe_rx.data := delayerSb.io.deq.bits + val delayNegEdge = withClock((!clock.asBool).asClock)(RegInit(false.B)) + delayNegEdge := delayerSb_clock.io.deq.bits.asBool && delayerSb_clock.io.deq.valid + io.sbAfe_rx.clk := Mux( + delayNegEdge, + clock.asBool, + false.B, + ) +} diff --git a/src/test/scala/e2e/AfeLoopbackTest.scala b/src/test/scala/e2e/AfeLoopbackTest.scala index e865ccab..1af3d9ff 100644 --- a/src/test/scala/e2e/AfeLoopbackTest.scala +++ b/src/test/scala/e2e/AfeLoopbackTest.scala @@ -20,81 +20,108 @@ import interfaces._ import protocol.{ProtocolLayerParams} import java.util.ResourceBundle -class AfeLoopbackTester (implicit p: Parameters) extends LazyModule { - val protoParams = ProtocolLayerParams() - val tlParams = TileLinkParams(address=0x0, addressRange=0xffff, configAddress=0x40000, inwardQueueDepth=8, outwardQueueDepth=8) - val fdiParams = FdiParams(width=64, dllpWidth=64, sbWidth=32) - val rdiParams = RdiParams(width=64, sbWidth=32) - val sbParams = SidebandParams() - val myId = 1 - val linkTrainingParams = LinkTrainingParams() - val afeParams = AfeParams() - val laneAsyncQueueParams = AsyncQueueParams() - val delay = 0.0 - val txns = 10 +class AfeLoopbackTester(implicit p: Parameters) extends LazyModule { + val protoParams = ProtocolLayerParams() + val tlParams = TileLinkParams( + address = 0x0, + addressRange = 0xffff, + configAddress = 0x40000, + inwardQueueDepth = 8, + outwardQueueDepth = 8, + ) + val fdiParams = FdiParams(width = 64, dllpWidth = 64, sbWidth = 32) + val rdiParams = RdiParams(width = 64, sbWidth = 32) + val sbParams = SidebandParams() + val myId = 1 + val linkTrainingParams = LinkTrainingParams() + val afeParams = AfeParams() + val laneAsyncQueueParams = AsyncQueueParams() + val delay = 0.0 + val txns = 100 - // Create clock source - val clockSourceNode = ClockSourceNode(Seq(ClockSourceParameters())) + // Create clock source + val clockSourceNode = ClockSourceNode(Seq(ClockSourceParameters())) - val csrfuzz = LazyModule(new TLFuzzer(txns)) - val fuzz = LazyModule(new TLFuzzer(txns)) - val tlUcieDie1 = LazyModule(new UCITLFront(tlParams=tlParams, - protoParams=protoParams, fdiParams=fdiParams, - rdiParams = rdiParams, - sbParams = sbParams, - myId = myId, - linkTrainingParams = linkTrainingParams, - afeParams = afeParams, - laneAsyncQueueParams = laneAsyncQueueParams)) - tlUcieDie1.clockNode := clockSourceNode - val ram = LazyModule(new TLRAM(AddressSet(tlParams.ADDRESS, tlParams.addressRange), beatBytes=tlParams.BEAT_BYTES)) + val csrfuzz = LazyModule(new TLFuzzer(txns)) + val fuzz = LazyModule(new TLFuzzer(txns)) + val tlUcieDie1 = LazyModule( + new UCITLFront( + tlParams = tlParams, + protoParams = protoParams, + fdiParams = fdiParams, + rdiParams = rdiParams, + sbParams = sbParams, + linkTrainingParams = linkTrainingParams, + afeParams = afeParams, + laneAsyncQueueParams = laneAsyncQueueParams, + ), + ) + tlUcieDie1.clockNode := clockSourceNode + val ram = LazyModule( + new TLRAM( + AddressSet(tlParams.ADDRESS, tlParams.addressRange), + beatBytes = tlParams.BEAT_BYTES, + ), + ) - // CSR node - tlUcieDie1.regNode.node := csrfuzz.node - // connect data nodes - tlUcieDie1.managerNode := TLSourceShrinker(tlParams.sourceIDWidth) := fuzz.node - ram.node := tlUcieDie1.clientNode - lazy val module = new Impl - class Impl extends LazyModuleImp(this) { - val io = IO(new Bundle { - val uci_clock = Input(new ClockBundle(ClockBundleParameters())) - val finished = Output(Bool()) - }) - // connect IOs - io.finished := fuzz.module.io.finished - val AfeLoopback = Module(new AfeLoopback(afeParams)) - io.uci_clock <> clockSourceNode.out(0)._1 - // inputs to tlUcieDie1 - tlUcieDie1.module.io.mbAfe <> AfeLoopback.io.mbAfe - tlUcieDie1.module.io.sbAfe <> AfeLoopback.io.sbAfe - } + // CSR node + tlUcieDie1.regNode.node := csrfuzz.node + // connect data nodes + tlUcieDie1.managerNode := TLSourceShrinker( + tlParams.sourceIDWidth, + ) := fuzz.node + ram.node := tlUcieDie1.clientNode + lazy val module = new Impl + class Impl extends LazyModuleImp(this) { + val io = IO(new Bundle { + val uci_clock = Input(new ClockBundle(ClockBundleParameters())) + val finished = Output(Bool()) + }) + // connect IOs + io.finished := fuzz.module.io.finished + val AfeLoopback = Module(new AfeLoopback(afeParams)) + io.uci_clock <> clockSourceNode.out(0)._1 + // inputs to tlUcieDie1 + // tlUcieDie1.module.io.mbAfe <> AfeLoopback.io.mbAfe + tlUcieDie1.module.io.mbAfe_tx.get <> AfeLoopback.io.mbAfe_tx + tlUcieDie1.module.io.mbAfe_rx.get <> AfeLoopback.io.mbAfe_rx + tlUcieDie1.module.io.txSbAfe <> AfeLoopback.io.sbAfe_tx + tlUcieDie1.module.io.rxSbAfe <> AfeLoopback.io.sbAfe_rx + + } } class AfeTLTestHarness(implicit val p: Parameters) extends Module { - val io = IO(new Bundle{val success = Output(Bool())}) + val io = IO(new Bundle { val success = Output(Bool()) }) val tester = Module(LazyModule(new AfeLoopbackTester).module) tester.io.uci_clock.clock := clock tester.io.uci_clock.reset := reset io.success := tester.io.finished // Dummy plusarg to avoid breaking verilator builds with emulator.cc - val useless_plusarg = PlusArg("useless_plusarg", width=1) + val useless_plusarg = PlusArg("useless_plusarg", width = 1) dontTouch(useless_plusarg) ElaborationArtefacts.add("plusArgs", PlusArgArtefacts.serialize_cHeader) } class AfeLoopbackTest extends AnyFlatSpec with ChiselScalatestTester { - behavior of "AfeLoopback" - val txns = 2 - val timeout = 1000 - implicit val p: Parameters = Parameters.empty - ignore should "finish request and response before timeout" in { - test(new AfeTLTestHarness()).withAnnotations(Seq(VcsBackendAnnotation, WriteVcdAnnotation)) {c => //.withAnnotations(Seq(VcsBackendAnnotation, WriteVcdAnnotation)) - println("start Afe Loopback Test") - c.clock.setTimeout(timeout+10) - c.clock.step(timeout) - c.io.success.expect(true.B) - println("Afe Loopback Test finished? " + c.io.success.peek()) - } + behavior of "AfeLoopback" + val txns = 2 + val timeout = 100000 + implicit val p: Parameters = Parameters.empty + it should "finish request and response before timeout" in { + test(new AfeTLTestHarness()).withAnnotations( + Seq(VcsBackendAnnotation, WriteVcdAnnotation), + ) { c => // .withAnnotations(Seq(VcsBackendAnnotation, WriteVcdAnnotation)) + + println("start Afe Loopback Test") + c.reset.poke(true.B) + c.clock.step(3) + c.reset.poke(false.B) + c.clock.setTimeout(timeout + 10) + c.clock.step(timeout) + c.io.success.expect(true.B) + println("Afe Loopback Test finished? " + c.io.success.peek()) } + } } diff --git a/src/test/scala/logphy/DataWidthValidFramerTest.scala b/src/test/scala/logphy/DataWidthValidFramerTest.scala new file mode 100644 index 00000000..3499535c --- /dev/null +++ b/src/test/scala/logphy/DataWidthValidFramerTest.scala @@ -0,0 +1,189 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor +import chiseltest._ +import org.scalatest.flatspec.AnyFlatSpec + +class DataWidthValidFramerTest extends AnyFlatSpec with ChiselScalatestTester { + val params = DataWidthCouplerParams( + inWidth = 4, + outWidth = 16, + ) + + val ratio = params.outWidth / params.inWidth + + behavior of "data width valid framer" + + it should "enqueue and dequeue data" in { + test(new DataWidthValidFramer(params)) { c => + initPorts(c) + c.io.in.enqueueNow("ha".U) + c.io.out.valid.expect(true.B) + c.io.in.enqueueNow("hb".U) + c.io.out.valid.expect(true.B) + c.io.in.enqueueNow("hc".U) + c.io.out.valid.expect(true.B) + c.io.in.enqueueNow("hd".U) + c.io.out.expectDequeueNow( + (new DataWithValid(params)).Lit( + _.data -> "hdcba".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(true.B)): _*, + ), + ), + ) + } + } + + it should "same cycle enqueue dequeue" in { + test(new DataWidthValidFramer(params)) { c => + c.io.out.valid.expect(false.B) + c.io.in.ready.expect(true.B) + c.io.in.valid.poke(true.B) + c.io.in.bits.poke("ha".U) + + c.clock.step() + + c.io.in.bits.poke("hb".U) + c.io.in.valid.poke(true.B) + c.io.out.ready.poke(true.B) + c.io.out.valid.expect(true.B) + c.io.out.bits.expect( + (new DataWithValid(params)).Lit( + _.data -> "h00ba".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B)): _*, + ), + ), + ) + + c.clock.step() + + c.io.in.bits.poke("hc".U) + c.io.in.valid.poke(true.B) + c.io.out.valid.expect(true.B) + c.io.out.ready.poke(false.B) + c.io.out.bits.expect( + (new DataWithValid(params)).Lit( + _.data -> "h000c".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B)): _*, + ), + ), + ) + + c.clock.step() + c.io.in.bits.poke("hd".U) + c.io.in.valid.poke(true.B) + c.io.out.valid.expect(true.B) + c.io.out.ready.poke(true.B) + c.io.out.bits.expect( + (new DataWithValid(params)).Lit( + _.data -> "h00dc".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B)): _*, + ), + ), + ) + } + } + + it should "one element enqueue dequeue" in { + test(new DataWidthValidFramer(params)) { c => + c.io.in.ready.expect(true.B) + c.io.in.bits.poke("ha".U) + c.io.in.valid.poke(true.B) + c.io.out.valid.expect(true.B) + c.io.out.ready.poke(true.B) + c.io.out.bits.expect( + (new DataWithValid(params)).Lit( + _.data -> "h000a".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B)): _*, + ), + ), + ) + + c.clock.step() + + c.io.in.ready.expect(true.B) + c.io.in.bits.poke("hb".U) + c.io.in.valid.poke(true.B) + c.io.out.valid.expect(true.B) + c.io.out.ready.poke(true.B) + c.io.out.bits.expect( + (new DataWithValid(params)).Lit( + _.data -> "h000b".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B)): _*, + ), + ), + ) + + c.clock.step() + + c.io.in.ready.expect(true.B) + c.io.in.bits.poke("hc".U) + c.io.in.valid.poke(true.B) + c.io.out.valid.expect(true.B) + c.io.out.ready.poke(false.B) + c.io.out.bits.expect( + (new DataWithValid(params)).Lit( + _.data -> "h000c".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B)): _*, + ), + ), + ) + + c.clock.step() + + c.io.in.bits.poke("hd".U) + c.io.in.valid.poke(true.B) + c.io.out.ready.poke(true.B) + c.io.out.valid.expect(true.B) + c.io.out.bits.expect( + (new DataWithValid(params)).Lit( + _.data -> "h00dc".U, + _.valid -> Vec.Lit( + (Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(true.B) ++ + Seq.fill(ratio)(false.B) ++ + Seq.fill(ratio)(false.B)): _*, + ), + ), + ) + + c.clock.step() + } + } + + private def initPorts(c: DataWidthValidFramer) = { + c.io.in.initSource().setSourceClock(c.clock) + c.io.out.initSink().setSinkClock(c.clock) + } +} diff --git a/src/test/scala/logphy/ErrorCounterTest.scala b/src/test/scala/logphy/ErrorCounterTest.scala new file mode 100644 index 00000000..7cd1eec7 --- /dev/null +++ b/src/test/scala/logphy/ErrorCounterTest.scala @@ -0,0 +1,229 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chisel3.util._ +import chiseltest._ +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor +import org.scalatest.flatspec.AnyFlatSpec +import interfaces.AfeParams +import scala.util.Random + +class ErrorCounterTest extends AnyFlatSpec with ChiselScalatestTester { + val afeParams = AfeParams() + val width = afeParams.mbSerializerRatio * afeParams.mbLanes * 4 + behavior of "error counter" + it should "correctly count errors for lfsr pattern" in { + test(new ErrorCounter(afeParams)) { c => + c.io.req.valid.poke(true.B) + c.io.req.bits.pattern.poke(TransmitPattern.LFSR) + + var lfsrVals = Seq( + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + ) + + /** No errors */ + c.io.req.bits.input.poke( + Vec.Lit(lfsrVals.map(_.U): _*), + ) + + val errWidth = log2Ceil(afeParams.mbSerializerRatio + 1) + c.io.errorCount.expect( + Vec.Lit( + Seq.fill(afeParams.mbLanes)( + 0.U(errWidth.W), + ): _*, + ), + ) + c.clock.step() + + val rand = new Random() + val bitWidth = afeParams.mbSerializerRatio + var numErrors = Seq.fill(16)(rand.nextInt(bitWidth)) + lfsrVals = Seq( + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + ) + println(f"numErrors: $numErrors") + + c.io.req.bits.input.poke( + Vec.Lit( + TestUtils + .makeRandomErrors(lfsrVals, numErrors, bitWidth) + .toSeq + .map(_.U(afeParams.mbSerializerRatio.W)): _*, + ), + ) + c.io.errorCount.expect(Vec.Lit(numErrors.map(_.U(errWidth.W)): _*)) + c.clock.step() + + numErrors = Seq.fill(16)(rand.nextInt(bitWidth)) + lfsrVals = Seq( + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + ) + println(f"numErrors: $numErrors") + + c.io.req.bits.input.poke( + Vec.Lit( + TestUtils + .makeRandomErrors(lfsrVals, numErrors, bitWidth) + .toSeq + .map(_.U(afeParams.mbSerializerRatio.W)): _*, + ), + ) + c.io.errorCount.expect(Vec.Lit(numErrors.map(_.U(errWidth.W)): _*)) + c.clock.step() + + c.io.req.valid.poke(false.B) + for (_ <- 0 until 10) { + c.clock.step() + } + + lfsrVals = Seq( + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + ) + + c.io.req.bits.input.poke( + Vec.Lit(lfsrVals.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ) + c.io.errorCount.expect( + Vec.Lit( + Seq.fill(afeParams.mbLanes)(0.U(errWidth.W)): _*, + ), + ) + } + } + it should "correctly count errors for valtrain pattern" in { + test(new ErrorCounter(afeParams)) { c => + c.io.req.valid.poke(true.B) + c.io.req.bits.pattern.poke(TransmitPattern.VALTRAIN) + + val valtrain = Seq.fill(afeParams.mbLanes)( + BigInt("11110000" * (afeParams.mbSerializerRatio / 8), 2), + ) + val errWidth = log2Ceil(afeParams.mbSerializerRatio + 1) + val bitWidth = afeParams.mbSerializerRatio + + /** No errors */ + c.io.req.bits.input.poke( + Vec.Lit(valtrain.map(_.U(bitWidth.W)): _*), + ) + c.io.errorCount.expect( + Vec.Lit(Seq.fill(afeParams.mbLanes)(0.U(errWidth.W)): _*), + ) + c.clock.step() + + val rand = new Random() + val numErrors = Seq.fill(16)(rand.nextInt(bitWidth)) + c.io.req.bits.input.poke( + Vec.Lit( + TestUtils + .makeRandomErrors(valtrain, numErrors, bitWidth) + .toSeq + .map(_.U(bitWidth.W)): _*, + ), + ) + c.io.errorCount.expect(Vec.Lit(numErrors.map(_.U(errWidth.W)): _*)) + c.clock.step() + + } + } + it should "correctly count errors for per-lane ID pattern" in { + test(new ErrorCounter(afeParams)) { c => + c.io.req.valid.poke(true.B) + c.io.req.bits.pattern.poke(TransmitPattern.PER_LANE_ID) + val errWidth = log2Ceil(afeParams.mbSerializerRatio + 1) + val bitWidth = afeParams.mbSerializerRatio + + val perLaneId = + Seq.tabulate(afeParams.mbLanes)(i => BigInt("A" + f"$i%02X" + "A", 16)) + + /** No errors */ + c.io.req.bits.input.poke( + Vec.Lit(perLaneId.map(_.U(bitWidth.W)): _*), + ) + c.io.errorCount.expect( + Vec.Lit( + Seq.fill(afeParams.mbLanes)(0.U(errWidth.W)): _*, + ), + ) + c.clock.step() + + val rand = new Random() + val numErrors = Seq.fill(16)(rand.nextInt(bitWidth)) + c.io.req.bits.input.poke( + Vec.Lit( + TestUtils + .makeRandomErrors(perLaneId, numErrors, bitWidth) + .toSeq + .map(_.U(bitWidth.W)): _*, + ), + ) + c.io.errorCount.expect( + Vec.Lit( + numErrors.map(_.U(errWidth.W)): _*, + ), + ) + c.clock.step() + } + } +} diff --git a/src/test/scala/logphy/LinkTrainingFSMTest.scala b/src/test/scala/logphy/LinkTrainingFSMTest.scala index 53f700fc..39da6a48 100644 --- a/src/test/scala/logphy/LinkTrainingFSMTest.scala +++ b/src/test/scala/logphy/LinkTrainingFSMTest.scala @@ -24,8 +24,13 @@ class LinkTrainingFSMTest extends AnyFlatSpec with ChiselScalatestTester { ) { c => initPorts(c) testTransitionOutOfReset(c) + println("Exited Reset") initSB(c) + println("Successfully initialized sideband") initMB(c) + println("Successfully initialized mainband") + // trainMB(c) + println("Successfully trained mainband") c.io.currentState.expect(LinkTrainingState.linkInit) @@ -52,13 +57,16 @@ class LinkTrainingFSMTest extends AnyFlatSpec with ChiselScalatestTester { private def initSB(c: LinkTrainingFSM): Unit = { c.io.currentState.expect(LinkTrainingState.sbInit) - c.io.sidebandFSMIO.patternTxData - .expectDequeue("h_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U) - c.io.sidebandFSMIO.patternTxData - .expectDequeue("h_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U) - c.io.sidebandFSMIO.rxData - .enqueueNow("h_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U) + val txData = Seq.fill(3)("h_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U) + val rxData = Seq.fill(1)("h_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U) + fork { + c.io.sidebandFSMIO.rxData.enqueueSeq(rxData) + }.fork { + c.io.sidebandFSMIO.patternTxData.expectDequeueSeq(txData) + }.join() + c.clock.step(5) + println("Exchanged clock patterns") c.io.sidebandFSMIO.rxData.enqueueNow( SBMessage_factory(SBM.SBINIT_OUT_OF_RESET, "PHY", true, "PHY", 0, 0).U, ) @@ -72,6 +80,7 @@ class LinkTrainingFSMTest extends AnyFlatSpec with ChiselScalatestTester { c.io.sidebandFSMIO.rxData.enqueue( SBMessage_factory(SBM.SBINIT_OUT_OF_RESET, "PHY", true, "PHY", 0, 0).U, ) + println("Exchanged out of reset sb packets") c.clock.step(2) c.io.sidebandFSMIO.rxData .enqueueNow( @@ -100,23 +109,20 @@ class LinkTrainingFSMTest extends AnyFlatSpec with ChiselScalatestTester { c.clock.step(3) } + // private def trainMB(c: LinkTrainingFSM): Unit = { + // c.io.currentState.expect(LinkTrainingState.mbTrain) + // } + private def testTransitionOutOfReset(c: LinkTrainingFSM): Unit = { c.io.currentState.expect(LinkTrainingState.reset) c.io.mainbandFSMIO.txFreqSel.expect(SpeedMode.speed4) c.clock.step() - c.io.mainbandFSMIO.pllLock.poke(true) - c.io.sidebandFSMIO.pllLock.poke(true) - for (_ <- 0 until 30) { c.io.currentState.expect(LinkTrainingState.reset) c.io.mainbandFSMIO.txFreqSel.expect(SpeedMode.speed4) c.clock.step() - c.io.mainbandFSMIO.pllLock.poke(false) - c.io.sidebandFSMIO.pllLock.poke(false) } - c.io.mainbandFSMIO.pllLock.poke(true) - c.io.sidebandFSMIO.pllLock.poke(true) c.clock.step() } diff --git a/src/test/scala/logphy/LogPhyLaneTest.scala b/src/test/scala/logphy/LogPhyLaneTest.scala index e8bb8ee5..48827dad 100644 --- a/src/test/scala/logphy/LogPhyLaneTest.scala +++ b/src/test/scala/logphy/LogPhyLaneTest.scala @@ -8,53 +8,50 @@ import freechips.rocketchip.util.AsyncQueueParams import org.scalatest.flatspec.AnyFlatSpec import interfaces._ +import scala.util.Random + class LogPhyLaneTest extends AnyFlatSpec with ChiselScalatestTester { val afeParams = AfeParams() + val afeParams32 = AfeParams(mbSerializerRatio = 32) val queueParams = new AsyncQueueParams() - behavior of "log phy TX lanes" + behavior of "log phy TX lanes no scramble" it should "correctly map TX bytes to their lanes" in { test(new SimLanes(afeParams, queueParams)) { c => - c.io.mainbandLaneIO.txData.initSource() - c.io.mainbandLaneIO.txData.setSourceClock(c.clock) - c.io.mainbandIo.txData.initSink() - c.io.mainbandIo.txData.setSinkClock(c.clock) + initPorts(c, false) - c.io.mainbandLaneIO.txData.enqueueNow( + c.io.mainbandIO.txData.enqueueNow( "h1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888".U, ) - c.io.mainbandIo.txData + c.io.mainbandLaneIO.txData .expectDequeueNow( Vec.Lit( - "h1211".U, - "h3411".U, - "h5622".U, - "h7822".U, - "h9a33".U, - "hbc33".U, - "hde44".U, - "hf044".U, - "h0f55".U, - "hed55".U, - "hcb66".U, - "ha966".U, - "h8777".U, - "h6577".U, - "h4388".U, "h2188".U, + "h4388".U, + "h6577".U, + "h8777".U, + "ha966".U, + "hcb66".U, + "hed55".U, + "h0f55".U, + "hf044".U, + "hde44".U, + "hbc33".U, + "h9a33".U, + "h7822".U, + "h5622".U, + "h3411".U, + "h1211".U, ), ) } } - behavior of "log phy RX lanes" + behavior of "log phy RX lanes no scramble" it should "correctly map RX bytes to their lanes" in { test(new SimLanes(afeParams, queueParams)) { c => - c.io.mainbandIo.rxData.initSource() - c.io.mainbandIo.rxData.setSourceClock(c.clock) - c.io.mainbandLaneIO.rxData.initSink() - c.io.mainbandLaneIO.rxData.setSinkClock(c.clock) + initPorts(c, scramble = false) - c.io.mainbandIo.rxData + c.io.mainbandLaneIO.rxData .enqueueNow( Vec.Lit( "h1211".U, @@ -75,12 +72,203 @@ class LogPhyLaneTest extends AnyFlatSpec with ChiselScalatestTester { "h2188".U, ), ) + c.io.mainbandIO.rxData.expectDequeueNow( + "h2143_6587_a9cb_ed0f_f0de_bc9a_7856_3412_8888_7777_6666_5555_4444_3333_2222_1111".U, + ) - c.io.mainbandLaneIO.rxData.expectDequeueNow( + } + } + + behavior of "log phy TX lanes scramble" + it should "correctly map TX bytes to their lanes" in { + test(new SimLanes(afeParams, queueParams)) { c => + initPorts(c, scramble = true) + + c.io.mainbandIO.txData.enqueueNow( "h1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888".U, ) + c.clock.step() + + c.io.mainbandLaneIO.txData + .expectDequeueNow( + Vec.Lit( + "b1001111000110100".U, // "h2188".U ^ "1011_1111_1011_1100".U, + "b100010000110011".U, // "h4388".U ^ "b0000_0111_1011_1011".U, + "b1010001000010111".U, // "h6577".U ^ "b1100011101100000".U + "b100011110101100".U, // "h8777".U ^ "0b1100000011011011".U + "b1010011001110100".U, // "ha966".U ^ "0b0000111100010010".U + "b10010101111".U, // "hcb66".U ^ "0b1100111111001001".U + "b1001101010011011".U, // "hed55".U ^ "0b0111011111001110".U + "b1011011101010010".U, // "h0f55".U ^ "0b1011100000000111".U + + "b100111111111000".U, // "hf044".U ^ "b1011_1111_1011_1100" + "b1101100111111111".U, // "hde44".U ^ "b0000_0111_1011_1011" + "b111101101010011".U, // "hbc33".U, ^ "b1100011101100000".U + "b101101011101000".U, // "h9a33".U ^ "0b1100000011011011".U + "b111011100110000".U, // "h7822".U, ^ "0b0000111100010010".U + "b1001100111101011".U, // "h5622".U, ^ "0b1100111111001001".U + "b100001111011111".U, // "h3411".U, ^ "0b0111011111001110".U + "b1010101000010110".U, // "h1211".U, ^ "0b1011100000000111".U + ), + ) + + println() + println() + println() + + c.io.mainbandIO.txData.enqueueNow( + "h1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888".U, + ) + + c.clock.step() + + c.io.mainbandLaneIO.txData + .expectDequeueNow( + Vec.Lit( + "b100110010101".U, // "h2188".U ^ "0b0010100000011101".U + "b1110111000001110".U, // "h4388".U ^ "0b1010110110000110".U + "b1101101101101001".U, // "h6577".U ^ "1011_1110_0001_1110".U + "b1001010011101111".U, // "h8777".U ^ "0001_0011_1001_1000".U + "b1110100001100111".U, // "ha966".U ^ "0100_0001_0000_0001".U + "b1001100111111111".U, // "hcb66".U ^ "0101001010011001".U + "b11101001010111".U, // "hed55".U ^ "1101011100000010".U + "b1000101011001110".U, // "h0f55".U ^ "1000010110011011".U + + "b1101100001011001".U, // "hf044".U ^ "0b0010100000011101" + "b111001111000010".U, // "hde44".U ^ "0b1010110110000110" + "b1000101101".U, // "hbc33".U, ^ "1011_1110_0001_1110".U + "b1000100110101011".U, // "h9a33".U ^ "0001_0011_1001_1000".U + "b11100100100011".U, // "h7822".U, ^ "0100_0001_0000_0001".U + "b10010111011".U, // "h5622".U, ^ "0101001010011001".U + "b1110001100010011".U, // "h3411".U, ^ "1101011100000010".U + "b1001011110001010".U, // "h1211".U, ^ "1000010110011011".U + ), + ) + } + } + + it should "tx data matches rx data" in { + test(new LanesLoopBack(afeParams, queueParams)) { c => + c.io.mainbandLaneIO.txData.initSource() + c.io.mainbandLaneIO.txData.setSourceClock(c.clock) + c.io.mainbandLaneIO.rxData.initSink() + c.io.mainbandLaneIO.rxData.setSinkClock(c.clock) + c.io.scramble.poke(true.B) + + val rand = new Random() + for (i <- 0 until 20) { + println("i: ", i) + val dataEnqueued = + BigInt(afeParams.mbLanes * afeParams.mbSerializerRatio, rand) + .U((afeParams.mbLanes * afeParams.mbSerializerRatio).W) + c.io.mainbandLaneIO.txData.enqueueNow( + dataEnqueued, + ) + c.clock.step() + c.io.mainbandLaneIO.rxData.expectDequeueNow( + dataEnqueued, + ) + c.clock.step() + + } + + } + } + + behavior of "log phy with serializer ratio 32" + it should "correctly map TX bytes to their lanes" in { + test(new SimLanes(afeParams32, queueParams)) { c => + initPorts(c, false) + c.io.mainbandIO.txData.enqueueNow( + ("h1234_5678_9abc_def0_0fed_cba9_8765_4321" + + "1111_2222_3333_4444_5555_6666_7777_8888" + + "1212_2323_3434_4545_5656_6767_7878_8989" + + "aaaa_bbbb_cccc_dddd_eeee_ffff_0000_1111").U, + ) + c.io.mainbandLaneIO.txData + .expectDequeueNow( + Vec.Lit( + "h21888911".U, + "h43888911".U, + "h65777800".U, + "h87777800".U, + "ha96667ff".U, + "hcb6667ff".U, + "hed5556ee".U, + "h0f5556ee".U, + "hf04445dd".U, + "hde4445dd".U, + "hbc3334cc".U, + "h9a3334cc".U, + "h782223bb".U, + "h562223bb".U, + "h341112aa".U, + "h121112aa".U, + ), + ) + } + } + it should "tx data matches rx data" in { + test(new LanesLoopBack(afeParams32, queueParams)) { c => + c.io.mainbandLaneIO.txData.initSource() + c.io.mainbandLaneIO.txData.setSourceClock(c.clock) + c.io.mainbandLaneIO.rxData.initSink() + c.io.mainbandLaneIO.rxData.setSinkClock(c.clock) + c.io.scramble.poke(true.B) + + val rand = new Random() + for (i <- 0 until 20) { + println("i: ", i) + val dataEnqueued = + BigInt(afeParams32.mbLanes * afeParams32.mbSerializerRatio, rand) + .U((afeParams32.mbLanes * afeParams32.mbSerializerRatio).W) + c.io.mainbandLaneIO.txData.enqueueNow( + dataEnqueued, + ) + c.clock.step() + c.io.mainbandLaneIO.rxData.expectDequeueNow( + dataEnqueued, + ) + c.clock.step() + + } + + } + } + + private def initPorts(c: SimLanes, scramble: Boolean): Unit = { + c.io.mainbandIO.txData.initSource() + c.io.mainbandIO.txData.setSourceClock(c.clock) + c.io.mainbandIO.rxData.initSink() + c.io.mainbandIO.rxData.setSinkClock(c.clock) + c.io.mainbandLaneIO.txData.initSink() + c.io.mainbandLaneIO.txData.setSinkClock(c.clock) + c.io.mainbandLaneIO.rxData.initSource() + c.io.mainbandLaneIO.rxData.setSourceClock(c.clock) + c.io.scramble.poke(scramble.B) + } + +} + +class LanesLoopBack( + afeParams: AfeParams, + queueParams: AsyncQueueParams, +) extends Module { + val io = IO(new Bundle { + val scramble = Input(Bool()) + val mainbandLaneIO = new MainbandIO(afeParams) + }) + val lanes = Module(new SimLanes(afeParams, queueParams)) + lanes.io.scramble := io.scramble + lanes.io.mainbandIO <> io.mainbandLaneIO + lanes.io.mainbandLaneIO.txData <> lanes.io.mainbandLaneIO.rxData + when(io.mainbandLaneIO.rxData.fire) { + printf("rxDataBits: %x\n", io.mainbandLaneIO.rxData.bits) + } + when(io.mainbandLaneIO.txData.fire) { + printf("txDataBits: %x\n", io.mainbandLaneIO.txData.bits) } } diff --git a/src/test/scala/logphy/LogPhyTest.scala b/src/test/scala/logphy/LogPhyTest.scala index 6d82948f..f62eb6d5 100644 --- a/src/test/scala/logphy/LogPhyTest.scala +++ b/src/test/scala/logphy/LogPhyTest.scala @@ -19,18 +19,17 @@ class LogPhyTest extends AnyFlatSpec with ChiselScalatestTester { * actual top-level test because Chiseltest does not support multiple clock * domains. */ - // behavior of "logical phy" - // it should "" in { - // test( - // new LogicalPhy( - // 0, - // linkTrainingParams = linkTrainingParams, - // afeParams = afeParams, - // rdiParams = rdiParams, - // fdiParams = fdiParams, - // sbParams = sbParams, - // laneAsyncQueueParams = laneAsyncQueueParams, - // ), - // ) { c => } - // } + behavior of "logical phy" + ignore should "" in { + test( + new LogicalPhy( + linkTrainingParams = linkTrainingParams, + afeParams = afeParams, + rdiParams = rdiParams, + fdiParams = fdiParams, + sbParams = sbParams, + laneAsyncQueueParams = laneAsyncQueueParams, + ), + ) { c => } + } } diff --git a/src/test/scala/logphy/MBInitFSMTest.scala b/src/test/scala/logphy/MBInitFSMTest.scala index 12fed833..128221ee 100644 --- a/src/test/scala/logphy/MBInitFSMTest.scala +++ b/src/test/scala/logphy/MBInitFSMTest.scala @@ -33,7 +33,7 @@ class MBInitFSMTest extends AnyFlatSpec with ChiselScalatestTester { if (req) SBM.MBINIT_PARAM_CONFIG_REQ else SBM.MBINIT_PARAM_CONFIG_RESP, src = "PHY", - remote = false, + remote = true, dst = "PHY", data = data, msgInfo = 0, @@ -63,14 +63,14 @@ class MBInitFSMTest extends AnyFlatSpec with ChiselScalatestTester { if (req) SBM.MBINIT_PARAM_CONFIG_REQ else SBM.MBINIT_PARAM_CONFIG_RESP, src = "PHY", - remote = false, + remote = true, dst = "PHY", data = data, msgInfo = 0, ).U, _.timeoutCycles -> (0.008 * sbClockFreq).toInt.U, - // _.reqType -> (if (req) MessageRequestType.MSG_REQ - // else MessageRequestType.MSG_RESP), + _.reqType -> MessageRequestType.EXCHANGE, + _.repeat -> false.B, ) msgReq } @@ -96,7 +96,7 @@ class MBInitFSMTest extends AnyFlatSpec with ChiselScalatestTester { behavior of "MBInitFSM" it should "perform parameter exchange -- basic sim" in { test( - new MBInitFSM(linkTrainingParams, afeParams), + new MBInitFSM(linkTrainingParams, afeParams, maxPatternCount = 1 << 32), ) { c => initializePorts(c) initialCheck(c) @@ -113,7 +113,7 @@ class MBInitFSMTest extends AnyFlatSpec with ChiselScalatestTester { behavior of "MBInitFSM" it should "perform parameter exchange with delays" in { test( - new MBInitFSM(linkTrainingParams, afeParams), + new MBInitFSM(linkTrainingParams, afeParams, maxPatternCount = 1 << 32), ) { c => initializePorts(c) initialCheck(c) @@ -139,7 +139,7 @@ class MBInitFSMTest extends AnyFlatSpec with ChiselScalatestTester { behavior of "MBInitFSM" it should "timeout" in { test( - new MBInitFSM(linkTrainingParams, afeParams), + new MBInitFSM(linkTrainingParams, afeParams, maxPatternCount = 1 << 32), ) { c => c.clock.setTimeout((0.008 * sbClockFreq).toInt + 20) initializePorts(c) @@ -249,7 +249,7 @@ class MBInitFSMTest extends AnyFlatSpec with ChiselScalatestTester { c.io.sbTrainIO.msgReq.initSink().setSinkClock(c.clock) c.io.sbTrainIO.msgReqStatus.initSource().setSourceClock(c.clock) c.io.patternGeneratorIO.transmitReq.initSink().setSinkClock(c.clock) - c.io.patternGeneratorIO.transmitPatternStatus + c.io.patternGeneratorIO.resp .initSource() .setSourceClock(c.clock) } diff --git a/src/test/scala/logphy/MBTrainerTest.scala b/src/test/scala/logphy/MBTrainerTest.scala new file mode 100644 index 00000000..85fab650 --- /dev/null +++ b/src/test/scala/logphy/MBTrainerTest.scala @@ -0,0 +1,399 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import interfaces._ +import chisel3._ +import chisel3.util._ +import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor +import chiseltest._ +import edu.berkeley.cs.ucie.digital.sideband.{SBM, SBMessage_factory} +import org.scalatest.flatspec.AnyFlatSpec + +class MBTrainerTest extends AnyFlatSpec with ChiselScalatestTester { + + val linkTrainingParams = LinkTrainingParams() + val afeParams = AfeParams() + val maxPatternCount = 1024 + val maxPatternCountWidth = log2Ceil(maxPatternCount + 1) + behavior of "mainband trainer test" + it should "correctly exchange SB out of reset message external trigger" in { + test(new MBTrainer(linkTrainingParams, afeParams, maxPatternCount)) { c => + initPorts(c) + var transmitPattern = TransmitPattern.LFSR + val sbClockFreq = + linkTrainingParams.sbClockFreqAnalog / afeParams.sbSerializerRatio + val timeoutCyclesDefault = (0.008 * sbClockFreq).toInt + var patternUICount = afeParams.mbLanes * afeParams.mbSerializerRatio * 4 + val maxErrors = 0 + + initialExpectedValues(c) + triggerOperation( + c, + transmitPattern, + patternUICount, + timeoutCyclesDefault, + maxErrors, + ) + + var errorCount = Vec.Lit( + Seq.fill(afeParams.mbLanes)(1.U(maxPatternCountWidth.W)): _*, + ) + completeTrainingOperation( + c, + transmitPattern, + timeoutCyclesDefault, + patternUICount, + errorCount, + ) + + transmitPattern = TransmitPattern.PER_LANE_ID + patternUICount = afeParams.mbLanes * afeParams.mbSerializerRatio + errorCount = Vec.Lit( + Seq.tabulate(afeParams.mbLanes)(x => x.U(maxPatternCountWidth.W)): _*, + ) + + triggerOperation( + c, + transmitPattern, + patternUICount, + timeoutCyclesDefault, + maxErrors, + ) + + completeTrainingOperation( + c, + transmitPattern, + timeoutCyclesDefault, + patternUICount, + errorCount, + ) + + triggerExit(c, timeoutCyclesDefault) + exitTraining(c, timeoutCyclesDefault) + + } + } + it should "correctly exchange SB out of reset message sideband trigger" in { + test(new MBTrainer(linkTrainingParams, afeParams, maxPatternCount)) { c => + initPorts(c) + var transmitPattern = TransmitPattern.LFSR + val sbClockFreq = + linkTrainingParams.sbClockFreqAnalog / afeParams.sbSerializerRatio + val timeoutCyclesDefault = (0.008 * sbClockFreq).toInt + var patternUICount = afeParams.mbLanes * afeParams.mbSerializerRatio * 4 + val maxErrors = 0 + + initialExpectedValues(c) + + /** Trigger operation via sideband */ + triggerOperationSB( + c, + timeoutCyclesDefault, + transmitPattern, + patternUICount, + maxErrors, + ) + + var errorCount = Vec.Lit( + Seq.fill(afeParams.mbLanes)(1.U(maxPatternCountWidth.W)): _*, + ) + + completeTrainingOperation( + c, + transmitPattern, + timeoutCyclesDefault, + patternUICount, + errorCount, + ) + + transmitPattern = TransmitPattern.PER_LANE_ID + patternUICount = afeParams.mbLanes * afeParams.mbSerializerRatio + errorCount = Vec.Lit( + Seq.tabulate(afeParams.mbLanes)(x => x.U(maxPatternCountWidth.W)): _*, + ) + + /** NOTE: cannot re-trigger operation with sideband as sideband is not + * expecting a begin training request + */ + triggerOperation( + c, + transmitPattern, + patternUICount, + timeoutCyclesDefault, + maxErrors, + ) + + completeTrainingOperation( + c, + transmitPattern, + timeoutCyclesDefault, + patternUICount, + errorCount, + ) + + /** Trigger exit via sideband */ + triggerExitSB(c, timeoutCyclesDefault) + + exitTraining(c, timeoutCyclesDefault) + + } + } + + private def triggerExitSB(c: MBTrainer, timeoutCyclesDefault: Int): Unit = { + + expectSBReq( + c = c, + bitPat = SBM.MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_REQ, + MessageRequestType.RECEIVE, + timeoutCyclesDefault, + msgInfo = 0, + data = 0, + ) + sbMsgSuccess(c) + } + + private def triggerOperationSB( + c: MBTrainer, + timeoutCyclesDefault: Int, + transmitPattern: TransmitPattern.Type, + patternUICount: Int, + maxErrors: Int, + ): Unit = { + val data = 0 | transmitPattern.litValue | (BigInt(patternUICount) << 43) + expectSBReq( + c, + SBM.MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_REQ, + MessageRequestType.RECEIVE, + timeoutCyclesDefault, + msgInfo = 0, + data = 0, + ) + sbMsgSuccess(c, data) + } + + private def initialExpectedValues(c: MBTrainer): Unit = { + c.io.patternGeneratorIO.transmitReq.expectInvalid() + c.io.patternGeneratorIO.resp.ready.expect(false.B) + c.io.sbTrainIO.msgReqStatus.ready.expect(false.B) + c.io.complete.expect(false.B) + c.io.trainingOperationIO.outputValid.expect(false.B) + c.io.err.expect(false.B) + c.io.sbMsgWrapperReset.expect(false.B) + c.clock.step() + } + + private def exitTraining(c: MBTrainer, timeoutCyclesDefault: Int): Unit = { + + expectSBReq( + c = c, + bitPat = SBM.MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_RESP, + reqType = MessageRequestType.EXCHANGE, + timeoutCyclesDefault = timeoutCyclesDefault, + msgInfo = 0, + data = 0, + ) + + sbMsgSuccess(c) + + c.clock.step() + c.io.complete.expect(true.B) + c.io.err.expect(false.B) + } + + private def triggerExit(c: MBTrainer, timeoutCyclesDefault: Int): Unit = { + c.io.trainingOperationIO.triggerExit.read.poke(true.B) + c.io.trainingOperationIO.triggerExit.write.expectDequeueNow(false.B) + c.io.trainingOperationIO.triggerExit.read.poke(false.B) + + /** Expect Pt Test End Test Req */ + expectSBReq( + c = c, + bitPat = SBM.MBTRAIN_END_TX_INIT_D_TO_C_POINT_TEST_REQ, + reqType = MessageRequestType.SEND, + timeoutCyclesDefault = timeoutCyclesDefault, + msgInfo = 0, + data = 0, + ) + + sbMsgSuccess(c) + } + + private def triggerOperation( + c: MBTrainer, + transmitPattern: TransmitPattern.Type, + patternUICount: Int, + timeoutCyclesDefault: Int, + maxErrors: Int, + ): Unit = { + c.io.trainingOperationIO.triggerNew.read.poke(true.B) + c.io.trainingOperationIO.pattern.poke(transmitPattern) + c.io.trainingOperationIO.patternUICount.poke(patternUICount.U) + c.io.trainingOperationIO.triggerExit.read.poke(false.B) + + c.io.trainingOperationIO.triggerNew.write.expectDequeueNow(false.B) + + c.io.trainingOperationIO.triggerNew.read.poke(false.B) + c.io.patternGeneratorIO.transmitReq.expectInvalid() + c.io.patternGeneratorIO.resp.ready.expect(false.B) + println("External trigger") + + val data = 0 | transmitPattern.litValue | (BigInt(patternUICount) << 43) + expectSBReq( + c, + SBM.MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_REQ, + reqType = MessageRequestType.SEND, + timeoutCyclesDefault, + maxErrors, + data, + ) + println("Received SB request for Point Test Start Req") + + /** Complete PTTest SB request */ + sbMsgSuccess(c) + } + + private def completeTrainingOperation( + c: MBTrainer, + transmitPattern: TransmitPattern.Type, + timeoutCyclesDefault: Int, + patternUICount: Int, + errorCount: Vec[UInt], + ): Unit = { + + println("********** BEGIN TRAINING OPERATION ***********") + + /** Expect PTTest SB response */ + expectSBReq( + c, + SBM.MBTRAIN_START_TX_INIT_D_TO_C_POINT_TEST_RESP, + reqType = MessageRequestType.EXCHANGE, + timeoutCyclesDefault, + msgInfo = 0, + data = 0, + ) + println("Received SB request for Point Test Start Resp") + + /** Complete PTTest SB response */ + sbMsgSuccess(c) + + /** Expect pattern generator request */ + c.io.sbTrainIO.msgReqStatus.ready.expect(false.B) + c.io.sbTrainIO.msgReq.expectInvalid() + c.io.patternGeneratorIO.resp.ready.expect(false.B) + c.io.patternGeneratorIO.transmitReq + .expectDequeue( + (chiselTypeOf(c.io.patternGeneratorIO.transmitReq.bits)).Lit( + _.pattern -> transmitPattern, + _.timeoutCycles -> timeoutCyclesDefault.U, + _.patternCountMax -> patternUICount.U, + _.patternDetectedCountMax -> patternUICount.U, + ), + ) + println("Received Pattern Generator request") + + /** Complete pattern generator request */ + c.io.sbTrainIO.msgReqStatus.ready.expect(false.B) + c.io.sbTrainIO.msgReq.expectInvalid() + c.io.patternGeneratorIO.resp.enqueueNow( + (chiselTypeOf(c.io.patternGeneratorIO.resp.bits)).Lit( + _.status -> MessageRequestStatusType.SUCCESS, + _.errorCount -> errorCount, + ), + ) + + c.io.trainingOperationIO.errorCounts.expect(errorCount) + + /** Expect Results Req req */ + expectSBReq( + c, + SBM.MBTRAIN_TX_INIT_D_TO_C_RESULTS_REQ, + reqType = MessageRequestType.EXCHANGE, + timeoutCyclesDefault, + msgInfo = 0, + data = 0, + ) + println("Received SB request for Results request") + + /** Complete Results req */ + sbMsgSuccess(c) + + /** Expect Results Resp req */ + expectSBReq( + c, + SBM.MBTRAIN_TX_INIT_D_TO_C_RESULTS_RESP, + reqType = MessageRequestType.EXCHANGE, + timeoutCyclesDefault, + msgInfo = 0, + data = 0, + ) + println("Received SB request for Results response") + + sbMsgSuccess(c) + + /** Now, the test should be waiting for either external intervention or a SB + * request... + */ + for (_ <- 0 until 10) { + c.clock.step() + c.io.patternGeneratorIO.transmitReq.expectInvalid() + c.io.patternGeneratorIO.resp.ready.expect(false.B) + c.io.complete.expect(false.B) + c.io.err.expect(false.B) + c.io.sbMsgWrapperReset.expect(false.B) + c.io.trainingOperationIO.outputValid.expect(true.B) + c.io.trainingOperationIO.errorCounts.expect(errorCount) + } + + } + + private def expectSBReq( + c: MBTrainer, + bitPat: BitPat, + reqType: MessageRequestType.Type, + timeoutCyclesDefault: Int, + msgInfo: Int, + data: BigInt, + ): Unit = { + c.io.sbTrainIO.msgReqStatus.ready.expect(false.B) + c.io.sbTrainIO.msgReq.expectDequeue( + (new MessageRequest).Lit( + _.msg -> (SBMessage_factory( + bitPat, + src = "PHY", + remote = true, + dst = "PHY", + data, + msgInfo = msgInfo, + )).U, + _.reqType -> reqType, + _.timeoutCycles -> timeoutCyclesDefault.U, + _.repeat -> false.B, + ), + ) + c.io.patternGeneratorIO.transmitReq.expectInvalid() + c.io.patternGeneratorIO.resp.ready.expect(false.B) + } + + private def sbMsgSuccess(c: MBTrainer, data: BigInt = 0): Unit = { + c.io.sbTrainIO.msgReqStatus.enqueueNow( + (new MessageRequestStatus).Lit( + _.data -> data.U, + _.status -> MessageRequestStatusType.SUCCESS, + ), + ) + } + + private def initPorts(c: MBTrainer): Unit = { + c.io.sbTrainIO.msgReq.initSink().setSinkClock(c.clock) + c.io.sbTrainIO.msgReqStatus.initSource().setSourceClock(c.clock) + c.io.patternGeneratorIO.transmitReq + .initSink() + .setSinkClock(c.clock) + c.io.patternGeneratorIO.resp + .initSource() + .setSourceClock(c.clock) + c.io.trainingOperationIO.triggerNew.write.initSink().setSinkClock(c.clock) + c.io.trainingOperationIO.triggerExit.write.initSink().setSinkClock(c.clock) + } +} diff --git a/src/test/scala/logphy/PatternGeneratorTest.scala b/src/test/scala/logphy/PatternGeneratorTest.scala index 2e158ee6..b1b4894b 100644 --- a/src/test/scala/logphy/PatternGeneratorTest.scala +++ b/src/test/scala/logphy/PatternGeneratorTest.scala @@ -2,8 +2,10 @@ package edu.berkeley.cs.ucie.digital package logphy import chisel3._ -import chisel3.experimental.BundleLiterals._ +import chisel3.util._ import chiseltest._ +import chisel3.experimental.BundleLiterals._ +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor import sideband.SidebandParams import interfaces._ import org.scalatest.flatspec.AnyFlatSpec @@ -11,21 +13,214 @@ import org.scalatest.flatspec.AnyFlatSpec class PatternGeneratorTest extends AnyFlatSpec with ChiselScalatestTester { val afeParams = AfeParams() val sbParams = SidebandParams() + val lfsrVals = Seq( + Seq( + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + ), + Seq( + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + ), + Seq( + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + ), + Seq( + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + ), + ) + behavior of "sideband pattern generator" it should "detect clock pattern no delay" in { - test(new PatternGenerator(afeParams = afeParams, sbParams = sbParams)) { - c => - initPorts(c) - testClockPatternSideband(c) + test( + new PatternGenerator( + afeParams = afeParams, + sbParams = sbParams, + maxPatternCount = 1024, + ), + ) { c => + initPorts(c) + testClockPatternSideband(c) } } - it should "detect clock pattern no delay twice" in { - test(new PatternGenerator(afeParams = afeParams, sbParams = sbParams)) { - c => - initPorts(c) - testClockPatternSideband(c) - testClockPatternSideband(c) + test( + new PatternGenerator( + afeParams = afeParams, + sbParams = sbParams, + maxPatternCount = 1024, + ), + ) { c => + initPorts(c) + testClockPatternSideband(c) + testClockPatternSideband(c) + } + } + behavior of "mainband pattern generator" + it should "detect MB LFSR pattern" in { + val maxPatternCount = 1024 + test( + new PatternGenerator( + afeParams = afeParams, + sbParams = sbParams, + maxPatternCount = maxPatternCount, + ), + ) { c => + initPorts(c) + val maxPatternCountWidth = log2Ceil(maxPatternCount + 1) + val width = afeParams.mbSerializerRatio * afeParams.mbLanes + + val expectedTx = lfsrVals + .map( + TestUtils + .lanesToOne(_, afeParams.mbLanes, afeParams.mbSerializerRatio), + ) + .map(_.U(width.W)) + var rxReceived = lfsrVals + .map( + TestUtils + .lanesToOne(_, afeParams.mbLanes, afeParams.mbSerializerRatio), + ) + + /** expected case with no errors */ + testMainband( + c = c, + transmitPattern = TransmitPattern.LFSR, + patternCountMax = 4, + patternDetectedCountMax = 4, + timeoutCycles = 80, + mainbandRx = rxReceived.map(f => f.U(width.W)), + mainbandTx = expectedTx, + expectedResult = MessageRequestStatusType.SUCCESS, + expectedErrorCount = Vec.Lit( + Seq.fill(afeParams.mbLanes)(0.U(maxPatternCountWidth.W)): _*, + ), + ) + + val numTests = 4 + for (_ <- 0 until numTests) { + val (rxRecv, err) = + TestUtils.createExpErrVecs(lfsrVals, afeParams.mbSerializerRatio) + testMainband( + c = c, + transmitPattern = TransmitPattern.LFSR, + patternCountMax = 4, + patternDetectedCountMax = 4, + timeoutCycles = 80, + mainbandTx = expectedTx, + mainbandRx = rxRecv + .map(f => + TestUtils + .lanesToOne( + f.toSeq, + afeParams.mbLanes, + afeParams.mbSerializerRatio, + ), + ) + .map(_.U), + expectedResult = MessageRequestStatusType.SUCCESS, + expectedErrorCount = Vec.Lit(err.map(_.U(maxPatternCountWidth.W)): _*), + ) + } + } + } + it should "handle MB timeouts" in { + val maxPatternCount = 1024 + test( + new PatternGenerator( + afeParams = afeParams, + sbParams = sbParams, + maxPatternCount = maxPatternCount, + ), + ) { c => + initPorts(c) + val width = afeParams.mbSerializerRatio * afeParams.mbLanes + val maxPatternCountWidth = log2Ceil(maxPatternCount + 1) + + val expectedTx = lfsrVals + .map( + TestUtils + .lanesToOne(_, afeParams.mbLanes, afeParams.mbSerializerRatio), + ) + .map(_.U(width.W)) + val rxReceived = lfsrVals + .map( + TestUtils + .lanesToOne(_, afeParams.mbLanes, afeParams.mbSerializerRatio), + ) + + /** expected case with no errors */ + testMainband( + c = c, + transmitPattern = TransmitPattern.LFSR, + patternCountMax = 4, + patternDetectedCountMax = 4, + timeoutCycles = 4, + mainbandRx = rxReceived.map(f => f.U(width.W)), + mainbandTx = expectedTx, + expectedResult = MessageRequestStatusType.ERR, + expectedErrorCount = Vec.Lit( + Seq.fill(afeParams.mbLanes)(0.U(maxPatternCountWidth.W)): _*, + ), + ) } } @@ -33,7 +228,7 @@ class PatternGeneratorTest extends AnyFlatSpec with ChiselScalatestTester { c.io.patternGeneratorIO.transmitReq .initSource() .setSourceClock(c.clock) - c.io.patternGeneratorIO.transmitPatternStatus + c.io.patternGeneratorIO.resp .initSink() .setSinkClock(c.clock) c.io.sidebandLaneIO.rxData @@ -42,22 +237,47 @@ class PatternGeneratorTest extends AnyFlatSpec with ChiselScalatestTester { c.io.sidebandLaneIO.txData .initSink() .setSinkClock(c.clock) + c.io.mainbandIO.rxData + .initSource() + .setSourceClock(c.clock) + c.io.mainbandIO.txData + .initSink() + .setSinkClock(c.clock) } - private def testClockPatternSideband(c: PatternGenerator): Unit = { + private def createRequest( + c: PatternGenerator, + transmitPattern: TransmitPattern.Type, + patternCountMax: Int, + patternDetectedCountMax: Int, + timeoutCycles: Int, + ): Unit = { c.io.patternGeneratorIO.transmitReq.ready.expect(true) c.io.sidebandLaneIO.rxData.ready.expect(false) + c.io.mainbandIO.rxData.ready.expect(false) c.io.sidebandLaneIO.txData.expectInvalid() - c.io.patternGeneratorIO.transmitPatternStatus.expectInvalid() - c.clock.step() - + c.io.mainbandIO.txData.expectInvalid() + c.io.patternGeneratorIO.resp.expectInvalid() c.io.patternGeneratorIO.transmitReq.enqueueNow( chiselTypeOf(c.io.patternGeneratorIO.transmitReq.bits).Lit( - _.pattern -> TransmitPattern.CLOCK_64_LOW_32, - _.timeoutCycles -> 80.U, - _.sideband -> true.B, + _.pattern -> transmitPattern, + _.patternCountMax -> patternCountMax.U, + _.patternDetectedCountMax -> patternDetectedCountMax.U, + _.timeoutCycles -> timeoutCycles.U, ), ) + } + + private def testClockPatternSideband(c: PatternGenerator): Unit = { + val length = 2 + + createRequest( + c, + TransmitPattern.CLOCK, + length * sbParams.sbNodeMsgWidth, + length * sbParams.sbNodeMsgWidth, + 80, + ) val testVector = Seq.fill(2)("haaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U) @@ -68,7 +288,74 @@ class PatternGeneratorTest extends AnyFlatSpec with ChiselScalatestTester { c.io.sidebandLaneIO.txData.expectDequeueSeq(testVector) }.join() - c.io.patternGeneratorIO.transmitPatternStatus - .expectDequeue(MessageRequestStatusType.SUCCESS) + c.io.patternGeneratorIO.resp.ready.poke(true.B) + + val resp = c.io.patternGeneratorIO.resp + val statusExpected = MessageRequestStatusType.SUCCESS + fork + .withRegion(Monitor) { + while (!resp.valid.peek().litToBoolean) { + c.clock.step(1) + } + resp.valid.expect(true.B) + resp.bits.expectPartial( + chiselTypeOf(resp.bits).Lit( + _.status -> statusExpected, + ), + ) + } + .joinAndStep(c.clock) + } + + private def testMainband( + c: PatternGenerator, + transmitPattern: TransmitPattern.Type, + patternCountMax: Int, + patternDetectedCountMax: Int, + timeoutCycles: Int, + mainbandRx: Seq[UInt], + mainbandTx: Seq[UInt], + expectedResult: MessageRequestStatusType.Type, + expectedErrorCount: Vec[UInt], + ): Unit = { + + createRequest( + c, + transmitPattern, + patternCountMax * afeParams.mbSerializerRatio * afeParams.mbLanes, + patternDetectedCountMax * afeParams.mbSerializerRatio * afeParams.mbLanes, + timeoutCycles, + ) + + fork { + c.io.mainbandIO.rxData.enqueueSeq(mainbandRx) + }.fork { + c.io.mainbandIO.txData.expectDequeueSeq(mainbandTx) + }.join() + + c.io.patternGeneratorIO.resp + .expectDequeue( + chiselTypeOf(c.io.patternGeneratorIO.resp.bits).Lit( + _.status -> expectedResult, + _.errorCount -> expectedErrorCount, + ), + ) + } } + +class PatternGeneratorLoopback( + afeParams: AfeParams, + sbParams: SidebandParams, + maxPatternCount: Int, +) extends Module { + + val io = IO(new Bundle { + val patternGeneratorIO = new PatternGeneratorIO(afeParams, maxPatternCount) + }) + + val patternGenerator = Module( + new PatternGenerator(afeParams, sbParams, maxPatternCount), + ) + +} diff --git a/src/test/scala/logphy/PatternReaderTest.scala b/src/test/scala/logphy/PatternReaderTest.scala new file mode 100644 index 00000000..6affe28a --- /dev/null +++ b/src/test/scala/logphy/PatternReaderTest.scala @@ -0,0 +1,350 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chisel3.util._ +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor +import sideband.SidebandParams +import interfaces._ +import chiseltest._ +import org.scalatest.flatspec.AnyFlatSpec + +import scala.collection.mutable + +class PatternReaderTest extends AnyFlatSpec with ChiselScalatestTester { + val afeParams = AfeParams() + val sbParams = SidebandParams() + + val lfsrVals = Seq( + Seq( + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + ), + Seq( + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + ), + Seq( + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + ), + Seq( + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + ), + ) + behavior of "pattern reader" + it should "detect SB clock pattern" in { + test(new PatternReader(sbParams, afeParams, 1024)) { c => + val maxPatternWidth = log2Ceil(1024 + 1) + initPorts(c) + createRequest( + c, + TransmitPattern.CLOCK, + sbParams.sbNodeMsgWidth * 4, + true, + maxPatternWidth, + ) + c.io.mbRxData.ready.expect(false.B) + + val testVector = + Seq.fill(4)("haaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa".U) + + testVector.foreach(vec => { + c.io.resp.complete.expect(false.B) + c.io.resp.inProgress.expect(true.B) + c.io.mbRxData.ready.expect(false.B) + c.io.sbRxData.enqueueNow(vec) + }) + + c.io.mbRxData.ready.expect(false.B) + c.io.resp.complete.expect(true.B) + } + } + it should "detect MB LFSR pattern no errors" in { + test(new PatternReader(sbParams, afeParams, maxPatternCount = 2048)) { c => + val maxPatternWidth = log2Ceil(2048 + 1) + + makeMBTest( + c, + TransmitPattern.LFSR, + lfsrVals.map(f => + Vec.Lit(f.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ), + errorCountExpected = + Vec.Lit(Seq.fill(afeParams.mbLanes)(0.U(maxPatternWidth.W)): _*), + maxErrorCountWidth = maxPatternWidth, + ) + } + } + it should "detect MB LFSR pattern error count" in { + test(new PatternReader(sbParams, afeParams, maxPatternCount = 2048)) { c => + val width = afeParams.mbLanes * afeParams.mbSerializerRatio + val maxPatternCountWidth = log2Ceil(2048 + 1) + var (testVecs, errVecs) = createExpErrVecs(lfsrVals) + + println(f"errVecs= $errVecs") + makeMBTest( + c, + TransmitPattern.LFSR, + testVecs.map(vec => + Vec.Lit(vec.toSeq.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ), + Vec.Lit(errVecs.map(_.U(maxPatternCountWidth.W)): _*), + maxPatternCountWidth, + ) + + c.reset.poke(true.B) + c.clock.step() + c.reset.poke(false.B) + + var res = createExpErrVecs(lfsrVals) + testVecs = res._1 + errVecs = res._2 + println(f"errVecs= $errVecs") + + makeMBTest( + c, + TransmitPattern.LFSR, + testVecs.map(vec => + Vec.Lit(vec.toSeq.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ), + Vec.Lit(errVecs.map(_.U(maxPatternCountWidth.W)): _*), + maxPatternCountWidth, + ) + + c.reset.poke(true.B) + c.clock.step() + c.reset.poke(false.B) + + res = createExpErrVecs(lfsrVals) + testVecs = res._1 + errVecs = res._2 + println(f"errVecs= $errVecs") + makeMBTest( + c, + TransmitPattern.LFSR, + testVecs.map(vec => + Vec.Lit(vec.toSeq.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ), + Vec.Lit(errVecs.map(_.U(maxPatternCountWidth.W)): _*), + maxPatternCountWidth, + ) + + } + } + it should "detect MB valtrain pattern" in { + test(new PatternReader(sbParams, afeParams, maxPatternCount = 2048)) { c => + var numVecs = 4 + val maxPatternCountWidth = log2Ceil(2048 + 1) + var testVector = Seq.fill(numVecs)( + Seq.fill(afeParams.mbLanes)( + BigInt("11110000" * (afeParams.mbSerializerRatio / 8), 2), + ), + ) + var (testVecs, errVecs) = createExpErrVecs(testVector) + + println(f"errVecs= $errVecs") + makeMBTest( + c, + TransmitPattern.VALTRAIN, + testVecs.map(vec => + Vec.Lit(vec.toSeq.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ), + Vec.Lit(errVecs.map(_.U(maxPatternCountWidth.W)): _*), + maxPatternCountWidth, + ) + + c.reset.poke(true.B) + c.clock.step() + c.reset.poke(false.B) + + numVecs = 10 + testVector = Seq.fill(numVecs)( + Seq.fill(afeParams.mbLanes)( + BigInt("11110000" * (afeParams.mbSerializerRatio / 8), 2), + ), + ) + + val res = createExpErrVecs(testVector) + testVecs = res._1 + errVecs = res._2 + + println(f"errVecs= $errVecs") + makeMBTest( + c, + TransmitPattern.VALTRAIN, + testVecs.map(vec => + Vec.Lit(vec.toSeq.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ), + Vec.Lit(errVecs.map(_.U(maxPatternCountWidth.W)): _*), + maxPatternCountWidth, + ) + + } + } + + private def createExpErrVecs( + testVector: Seq[Seq[BigInt]], + ): (Seq[mutable.Seq[BigInt]], Seq[Int]) = { + val testErrSeq = testVector.map(vec => + TestUtils.makeRandomErrors(vec, afeParams.mbSerializerRatio), + ) + val testVecs = testErrSeq.map(_._1) + val errVecs = + testErrSeq.map(_._2).reduce((x, y) => x.zip(y).map(f => f._1 + f._2)) + (testVecs, errVecs) + } + + it should "detect MB per-lane ID pattern" in { + test(new PatternReader(sbParams, afeParams, maxPatternCount = 2048)) { c => + val numVecs = 5 + val maxPatternCountWidth = log2Ceil(2048 + 1) + val testVector = Seq.fill(numVecs)( + Seq.tabulate(afeParams.mbLanes)(i => BigInt("A" + f"$i%02X" + "A", 16)), + ) + + val (testVecs, errVecs) = createExpErrVecs(testVector) + + println(f"errVecs= $errVecs") + makeMBTest( + c, + TransmitPattern.PER_LANE_ID, + testVecs.map(vec => + Vec.Lit(vec.toSeq.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ), + Vec.Lit(errVecs.map(_.U(maxPatternCountWidth.W)): _*), + maxPatternCountWidth, + ) + + c.reset.poke(true.B) + c.clock.step() + c.reset.poke(false.B) + + } + } + + private def toggleBits( + input: BigInt, + bits: Int*, + ): BigInt = { + var result = input + for (bit <- bits) { + result ^= BigInt(1) << bit + } + BigInt(result.toString(2), 2) + } + + private def makeMBTest( + c: PatternReader, + transmitPattern: TransmitPattern.Type, + testVector: Seq[Vec[UInt]], + errorCountExpected: Vec[UInt], + maxErrorCountWidth: Int, + ): Unit = { + initPorts(c) + val width = afeParams.mbLanes * afeParams.mbSerializerRatio + createRequest( + c, + transmitPattern, + patternCountMax = width * testVector.length, + sideband = false, + maxErrorCountWidth, + ) + c.io.sbRxData.ready.expect(false.B) + + testVector.foreach(vec => { + c.io.resp.complete.expect(false.B) + c.io.resp.inProgress.expect(true.B) + c.io.sbRxData.ready.expect(false.B) + c.io.mbRxData.enqueueNow(vec) + }) + + c.io.sbRxData.ready.expect(false.B) + c.io.resp.complete.expect(true.B) + c.io.resp.errorCount.expect(errorCountExpected) + } + private def createRequest( + c: PatternReader, + transmitPattern: TransmitPattern.Type, + patternCountMax: Int, + sideband: Boolean, + maxPatternWidth: Int, + ): Unit = { + c.io.request.bits.pattern.poke(transmitPattern) + c.io.request.bits.patternCountMax.poke(patternCountMax) + c.io.request.valid.poke(true.B) + c.io.resp.complete.expect(false.B) + c.io.resp.errorCount.expect( + Vec.Lit( + Seq.fill(afeParams.mbLanes)(0.U(maxPatternWidth.W)): _*, + ), + ) + c.io.resp.inProgress.expect(false.B) + c.clock.step() + } + + private def initPorts(c: PatternReader): Unit = { + c.io.sbRxData.initSource() + c.io.sbRxData.setSourceClock(c.clock) + c.io.mbRxData.initSource() + c.io.mbRxData.setSourceClock(c.clock) + } + +} diff --git a/src/test/scala/logphy/PatternWriterTest.scala b/src/test/scala/logphy/PatternWriterTest.scala new file mode 100644 index 00000000..c2246a2d --- /dev/null +++ b/src/test/scala/logphy/PatternWriterTest.scala @@ -0,0 +1,228 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chisel3.experimental.VecLiterals.AddObjectLiteralConstructor +import sideband.SidebandParams +import interfaces._ +import chiseltest._ +import org.scalatest.flatspec.AnyFlatSpec + +class PatternWriterTest extends AnyFlatSpec with ChiselScalatestTester { + val afeParams = AfeParams() + val sbParams = SidebandParams() + behavior of "sideband pattern writer" + it should "send SB clock pattern" in { + test(new PatternWriter(sbParams, afeParams, maxPatternCount = 2048)) { c => + initPorts(c) + createRequest( + c, + TransmitPattern.CLOCK, + patternCountMax = 512, + ) + + c.clock.step() + for (_ <- 0 until 512 / sbParams.sbNodeMsgWidth) { + c.io.mbTxData.expectInvalid() + c.io.resp.complete.expect(false.B) + c.io.resp.inProgress.expect(true.B) + c.io.sbTxData.expectDequeueNow( + ("h" + "aaaa" * (sbParams.sbNodeMsgWidth / 16)).U, + ) + } + + c.io.resp.complete.expect(true.B) + + } + } + + val lfsrVals = Seq( + Seq( + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + BigInt("bfbc", 16), + BigInt("07bb", 16), + BigInt("c760", 16), + BigInt("c0db", 16), + BigInt("0f12", 16), + BigInt("cfc9", 16), + BigInt("77ce", 16), + BigInt("b807", 16), + ), + Seq( + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + BigInt("281d", 16), + BigInt("ad86", 16), + BigInt("be1e", 16), + BigInt("1398", 16), + BigInt("4101", 16), + BigInt("5299", 16), + BigInt("d702", 16), + BigInt("859b", 16), + ), + Seq( + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + BigInt("28b8", 16), + BigInt("84d3", 16), + BigInt("e496", 16), + BigInt("6045", 16), + BigInt("b083", 16), + BigInt("d0c6", 16), + BigInt("7cad", 16), + BigInt("ac6b", 16), + ), + Seq( + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + BigInt("8c54", 16), + BigInt("3f5e", 16), + BigInt("2bc1", 16), + BigInt("149f", 16), + BigInt("b083", 16), + BigInt("a41c", 16), + BigInt("1716", 16), + BigInt("b30a", 16), + ), + ) + it should "send MB LFSR pattern" in { + test(new PatternWriter(sbParams, afeParams, maxPatternCount = 2048)) { c => + initPorts(c) + createRequest( + c, + TransmitPattern.LFSR, + afeParams.mbSerializerRatio * afeParams.mbLanes * 4, + ) + + val lfsrValVecs = lfsrVals.map(f => + Vec.Lit(f.map(_.U(afeParams.mbSerializerRatio.W)): _*), + ) + + c.clock.step() + for (i <- 0 until 4) { + c.io.sbTxData.expectInvalid() + c.io.resp.complete.expect(false.B) + c.io.resp.inProgress.expect(true.B) + c.io.mbTxData.expectDequeueNow( + lfsrValVecs(i), + ) + } + + c.io.resp.complete.expect(true.B) + + } + } + + it should "send MB valtrain pattern" in { + val maxPatternCount = 2048 + test( + new PatternWriter(sbParams, afeParams, maxPatternCount = maxPatternCount), + ) { c => + initPorts(c) + val patternCountMax = 512 + createRequest( + c, + TransmitPattern.VALTRAIN, + patternCountMax = patternCountMax, + ) + + val numVecs = + patternCountMax / (afeParams.mbLanes * afeParams.mbSerializerRatio) + + val valtrain = Seq.fill(numVecs)( + Seq.fill(afeParams.mbLanes)( + BigInt("11110000" * (afeParams.mbSerializerRatio / 8), 2), + ), + ) + + c.clock.step() + for (i <- 0 until numVecs) { + c.io.sbTxData.expectInvalid() + c.io.resp.complete.expect(false.B) + c.io.resp.inProgress.expect(true.B) + c.io.mbTxData.expectDequeueNow( + Vec.Lit(valtrain(i).map(_.U(afeParams.mbSerializerRatio.W)): _*), + ) + } + + c.io.resp.complete.expect(true.B) + + } + } + + it should "send MB per-lane ID pattern" in { + test(new PatternWriter(sbParams, afeParams, maxPatternCount = 2048)) { c => + initPorts(c) + val numVecs = 5 + val perLaneVec = Seq.fill(numVecs)( + Seq.tabulate(afeParams.mbLanes)(i => BigInt("A" + f"$i%02X" + "A", 16)), + ) + + createRequest( + c, + TransmitPattern.PER_LANE_ID, + numVecs * afeParams.mbSerializerRatio * afeParams.mbLanes, + ) + + c.clock.step() + for (i <- 0 until numVecs) { + c.io.sbTxData.expectInvalid() + c.io.resp.complete.expect(false.B) + c.io.resp.inProgress.expect(true.B) + c.io.mbTxData.expectDequeueNow( + Vec.Lit(perLaneVec(i).map(_.U(afeParams.mbSerializerRatio.W)): _*), + ) + } + + c.io.resp.complete.expect(true.B) + + } + } + + private def createRequest( + c: PatternWriter, + transmitPattern: TransmitPattern.Type, + patternCountMax: Int, + ): Unit = { + c.io.request.valid.poke(true.B) + c.io.request.bits.pattern.poke(transmitPattern) + c.io.request.bits.patternCountMax.poke(patternCountMax.U) + c.io.resp.complete.expect(false.B) + c.io.resp.inProgress.expect(false.B) + c.io.sbTxData.expectInvalid() + c.io.mbTxData.expectInvalid() + } + + private def initPorts(c: PatternWriter) = { + c.io.mbTxData.initSink() + c.io.mbTxData.setSinkClock(c.clock) + c.io.sbTxData.initSink() + c.io.sbTxData.setSinkClock(c.clock) + } + +} diff --git a/src/test/scala/logphy/RdiDataMapperTest.scala b/src/test/scala/logphy/RdiDataMapperTest.scala index eaebcde8..df0d2099 100644 --- a/src/test/scala/logphy/RdiDataMapperTest.scala +++ b/src/test/scala/logphy/RdiDataMapperTest.scala @@ -13,28 +13,31 @@ class RdiDataMapperTest extends AnyFlatSpec with ChiselScalatestTester { behavior of "rdi data mapper" it should "correctly output rx lane data" in { test(new RdiDataMapper(rdiParams, afeParams)) { c => - val data = Vec.Lit( + val data = Seq( "h1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888".U, "h2222_2222_3333_4444_5555_6666_8888_8888_2234_5688_9abc_def0_0fed_cba9_8865_4322".U, "h1244_6678_9abc_def0_0fed_cba9_8766_4421_1111_2222_4444_4444_6666_6666_7777_8888".U, "h1111_3333_3333_4444_5555_6666_7777_8888_1334_5678_9aac_def0_0fed_caa9_8765_4331".U, ) - val dataUInt = - "h1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888_2222_2222_3333_4444_5555_6666_8888_8888_2234_5688_9abc_def0_0fed_cba9_8865_4322_1244_6678_9abc_def0_0fed_cba9_8766_4421_1111_2222_4444_4444_6666_6666_7777_8888_1111_3333_3333_4444_5555_6666_7777_8888_1334_5678_9aac_def0_0fed_caa9_8765_4331".U - c.io.mainbandLaneIO.rxData.initSource() - c.io.mainbandLaneIO.rxData.setSourceClock(c.clock) + val dataUInt = ("h" + + "1111_3333_3333_4444_5555_6666_7777_8888_1334_5678_9aac_def0_0fed_caa9_8765_4331" + + "1244_6678_9abc_def0_0fed_cba9_8766_4421_1111_2222_4444_4444_6666_6666_7777_8888" + + "2222_2222_3333_4444_5555_6666_8888_8888_2234_5688_9abc_def0_0fed_cba9_8865_4322" + + "1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888").U - c.io.mainbandLaneIO.rxData.valid.poke(false) + c.io.mainbandIO.rxData.initSource() + c.io.mainbandIO.rxData.setSourceClock(c.clock) + + c.io.mainbandIO.rxData.valid.poke(false) c.clock.step() for (i: Int <- 0 until 4) { - c.io.mainbandLaneIO.rxData.valid.poke(false) for (_: Int <- 0 until 10) { c.io.rdi.plData.valid.expect(false) c.clock.step() } - c.io.mainbandLaneIO.rxData.enqueueNow(data(i)) + c.io.mainbandIO.rxData.enqueueNow(data(i)) } - c.io.rdi.plData.valid.expect(true) + c.io.rdi.plData.valid.expect(true.B) c.io.rdi.plData.bits.expect(dataUInt) c.clock.step() for (_ <- 0 until 10) { @@ -46,19 +49,22 @@ class RdiDataMapperTest extends AnyFlatSpec with ChiselScalatestTester { it should "correctly output tx lane data" in { test(new RdiDataMapper(rdiParams, afeParams)) { c => - val data = Vec.Lit( + val data = Seq( "h1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888".U, "h2222_2222_3333_4444_5555_6666_8888_8888_2234_5688_9abc_def0_0fed_cba9_8865_4322".U, "h1244_6678_9abc_def0_0fed_cba9_8766_4421_1111_2222_4444_4444_6666_6666_7777_8888".U, "h1111_3333_3333_4444_5555_6666_7777_8888_1334_5678_9aac_def0_0fed_caa9_8765_4331".U, ) - val dataUInt = - "h1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888_2222_2222_3333_4444_5555_6666_8888_8888_2234_5688_9abc_def0_0fed_cba9_8865_4322_1244_6678_9abc_def0_0fed_cba9_8766_4421_1111_2222_4444_4444_6666_6666_7777_8888_1111_3333_3333_4444_5555_6666_7777_8888_1334_5678_9aac_def0_0fed_caa9_8765_4331".U + val dataUInt = ("h" + + "1111_3333_3333_4444_5555_6666_7777_8888_1334_5678_9aac_def0_0fed_caa9_8765_4331" + + "1244_6678_9abc_def0_0fed_cba9_8766_4421_1111_2222_4444_4444_6666_6666_7777_8888" + + "2222_2222_3333_4444_5555_6666_8888_8888_2234_5688_9abc_def0_0fed_cba9_8865_4322" + + "1234_5678_9abc_def0_0fed_cba9_8765_4321_1111_2222_3333_4444_5555_6666_7777_8888").U - c.io.mainbandLaneIO.txData.initSink() - c.io.mainbandLaneIO.txData.setSinkClock(c.clock) + c.io.mainbandIO.txData.initSink() + c.io.mainbandIO.txData.setSinkClock(c.clock) - c.io.mainbandLaneIO.txData.valid.expect(false.B) + c.io.mainbandIO.txData.valid.expect(false.B) c.io.rdi.lpData.valid.poke(false.B) c.io.rdi.lpData.irdy.expect(false.B) c.io.rdi.lpData.ready.expect(true.B) @@ -73,16 +79,16 @@ class RdiDataMapperTest extends AnyFlatSpec with ChiselScalatestTester { c.io.rdi.lpData.valid.poke(false.B) for (i: Int <- 0 until 4) { - c.io.mainbandLaneIO.txData.ready.poke(false) + c.io.mainbandIO.txData.ready.poke(false) for (_: Int <- 0 until 10) { c.io.rdi.lpData.ready.expect(false) c.clock.step() } - c.io.mainbandLaneIO.txData.expectDequeueNow(data(i)) + c.io.mainbandIO.txData.expectDequeueNow(data(i)) } for (_ <- 0 until 10) { - c.io.mainbandLaneIO.txData.valid.expect(false) + c.io.mainbandIO.txData.valid.expect(false) c.io.rdi.lpData.ready.expect(true) c.clock.step() } diff --git a/src/test/scala/logphy/SBMsgWrapperTest.scala b/src/test/scala/logphy/SBMsgWrapperTest.scala index 1bc68789..f2979ddd 100644 --- a/src/test/scala/logphy/SBMsgWrapperTest.scala +++ b/src/test/scala/logphy/SBMsgWrapperTest.scala @@ -4,10 +4,11 @@ package logphy import chisel3._ import chisel3.experimental.BundleLiterals._ import chiseltest._ -import edu.berkeley.cs.ucie.digital.sideband.SidebandParams import org.scalatest.flatspec.AnyFlatSpec import sideband._ +import scala.util.Random + class SBMsgWrapperTest extends AnyFlatSpec with ChiselScalatestTester { val sbParams = SidebandParams() behavior of "sb message wrapper" @@ -32,6 +33,59 @@ class SBMsgWrapperTest extends AnyFlatSpec with ChiselScalatestTester { } } + it should "correctly receive only" in { + test(new SBMsgWrapper(sbParams)) { c => + initPorts(c) + testSBInitReceiveOnly(c) + testSBInitReceiveOnly(c) + } + + } + private def testSBInitReceiveOnly(c: SBMsgWrapper): Unit = { + c.io.laneIO.rxData.ready.expect(false) + c.io.laneIO.txData.expectInvalid() + c.io.trainIO.msgReq.ready.expect(true) + c.io.trainIO.msgReqStatus.expectInvalid() + + c.clock.step() + val rand = new Random() + val data = BigInt(64, rand) + val sbMsg = SBMessage_factory( + SBM.SBINIT_OUT_OF_RESET, + "PHY", + true, + "PHY", + data = data, + msgInfo = 0, + ) + c.io.trainIO.msgReq.enqueueNow( + (new MessageRequest).Lit( + _.msg -> sbMsg.U, + _.reqType -> MessageRequestType.RECEIVE, + _.timeoutCycles -> 80.U, + _.repeat -> true.B, + ), + ) + + c.io.trainIO.msgReq.ready.expect(false) + for (_ <- 0 until 4) { + c.clock.step() + c.io.laneIO.txData.expectInvalid() + } + c.io.laneIO.rxData.enqueueNow(sbMsg.U) + for (_ <- 0 until 4) { + c.clock.step() + c.io.trainIO.msgReqStatus.valid.expect(true.B) + c.io.laneIO.txData.expectInvalid() + } + c.io.trainIO.msgReqStatus.expectDequeue( + (new MessageRequestStatus).Lit( + _.status -> + MessageRequestStatusType.SUCCESS, + _.data -> data.U, + ), + ) + } private def testSBInitOutOfReset(c: SBMsgWrapper): Unit = { c.io.laneIO.rxData.ready.expect(false) c.io.laneIO.txData.expectInvalid() @@ -50,8 +104,9 @@ class SBMsgWrapperTest extends AnyFlatSpec with ChiselScalatestTester { c.io.trainIO.msgReq.enqueueNow( (new MessageRequest).Lit( _.msg -> sbMsg.U, - // _.reqType -> MessageRequestType.MSG_EXCH, + _.reqType -> MessageRequestType.EXCHANGE, _.timeoutCycles -> 80.U, + _.repeat -> true.B, ), ) diff --git a/src/test/scala/logphy/ScramblerTest.scala b/src/test/scala/logphy/ScramblerTest.scala new file mode 100644 index 00000000..9eb37b3e --- /dev/null +++ b/src/test/scala/logphy/ScramblerTest.scala @@ -0,0 +1,59 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import chisel3._ +import chiseltest._ +import edu.berkeley.cs.ucie.digital.interfaces.AfeParams +import org.scalatest.funspec.AnyFunSpec + +class ScramblerTest extends AnyFunSpec with ChiselScalatestTester { + + describe("Scrambler") { + it("dummy test") { + test(new UCIeScrambler(afeParams = AfeParams(), numLanes = 16)) { c => + c.io.valid.poke(true.B) + for (i <- 0 until 16) { + c.io.data_in(i).poke(0.U) + } + for (_ <- 0 until 4) { + println() + c.io.valid.poke(true.B) + for (i <- 0 until 16) { + println(c.io.data_out(i).peek().litValue.toString(16)) + } + c.clock.step() + } + } + } + + // it("4 lane scrambler test") { + // test(new UCIeScrambler(afeParams = AfeParams(), numLanes = 4)) { c => + // c.reset.poke(true.B) + // c.clock.step() + // c.clock.step() + // c.reset.poke(false.B) + // c.clock.step() + // c.io.valid.poke(true.B) + + // c.io.data_in(0).poke(1.U(16.W)) + // c.io.data_in(1).poke(1012.U(16.W)) + // c.io.data_in(2).poke(823.U(16.W)) + // c.io.data_in(3).poke(134.U(16.W)) + + // c.io.data_out(0).expect(49085.U(16.W)) + // c.io.data_out(1).expect(1103.U(16.W)) + // c.io.data_out(2).expect(50263.U(16.W)) + // c.io.data_out(3).expect(49245.U(16.W)) + // c.clock.step() + // c.io.data_in(0).poke(203.U(16.W)) + // c.io.data_in(1).poke(176.U(16.W)) + // c.io.data_in(2).poke(21.U(16.W)) + // c.io.data_in(3).poke(5847.U(16.W)) + // c.io.data_out(0).expect(65321.U(16.W)) + // c.io.data_out(1).expect(56489.U(16.W)) + // c.io.data_out(2).expect(11245.U(16.W)) + // c.io.data_out(3).expect(57654.U(16.W)) + // } + // } + } +} diff --git a/src/test/scala/logphy/TestUtils.scala b/src/test/scala/logphy/TestUtils.scala new file mode 100644 index 00000000..8aa6db13 --- /dev/null +++ b/src/test/scala/logphy/TestUtils.scala @@ -0,0 +1,113 @@ +package edu.berkeley.cs.ucie.digital +package logphy + +import scala.collection.mutable +import scala.util.Random + +object TestUtils { + + def createExpErrVecs( + testVector: Seq[Seq[BigInt]], + serializerRatio: Int, + ): (Seq[mutable.Seq[BigInt]], Seq[Int]) = { + val testErrSeq = + testVector.map(vec => TestUtils.makeRandomErrors(vec, serializerRatio)) + val testVecs = testErrSeq.map(_._1) + val errVecs = + testErrSeq.map(_._2).reduce((x, y) => x.zip(y).map(f => f._1 + f._2)) + (testVecs, errVecs) + } + + def oneToLanes( + input: BigInt, + numLanes: Int, + serializerRatio: Int, + ): Seq[BigInt] = { + val result = mutable.Seq.fill(numLanes)(BigInt(0)) + var one = input + for (i <- 0 until serializerRatio / 8) { + for (j <- 0 until numLanes) { + result(j) |= (one & 0xff) << (i * 8) + one >>= 8 + } + } + result.toSeq + } + + def lanesToOne( + input: Seq[BigInt], + numLanes: Int, + serializerRatio: Int, + ): BigInt = { + var result = BigInt(0) + val lanes = mutable.Seq(input: _*) + for (i <- 0 until serializerRatio / 8) { + for (j <- 0 until numLanes) { + result |= (lanes(j) & 0xff) << ((i * numLanes + j) * 8) + lanes(j) >>= 8 + } + } + result + } + + def makeRandomErrors( + input: Seq[BigInt], + width: Int, + ): (mutable.Seq[BigInt], Seq[Int]) = { + val result = mutable.Seq(input: _*) + val rand = new Random() + val numErrors = Seq.fill(input.length)(rand.nextInt(width)) + for (i <- 0 until input.length) { + val errorBits = mutable.Set[Int]() + val numError = numErrors(i) + while (errorBits.size < numError) { + val bit = rand.nextInt(width) + if (!errorBits.contains(bit)) { + result(i) ^= BigInt(1) << bit + errorBits += bit + } + } + } + (result, numErrors) + } + + def makeRandomErrors( + input: Seq[BigInt], + numErrors: Seq[Int], + width: Int, + ): mutable.Seq[BigInt] = { + val result = mutable.Seq(input: _*) + val rand = new Random() + for (i <- 0 until input.length) { + val errorBits = mutable.Set[Int]() + val numError = numErrors(i) + while (errorBits.size < numError) { + val bit = rand.nextInt(width) + if (!errorBits.contains(bit)) { + result(i) ^= BigInt(1) << bit + errorBits += bit + } + } + } + result + } + def makeRandomErrors( + input: Seq[BigInt], + numErrors: Int, + width: Int, + ): mutable.Seq[BigInt] = { + val result = mutable.Seq(input: _*) + val errorBits = mutable.Set[(Int, Int)]() + + val rand = new Random() + while (errorBits.size < numErrors) { + val i = rand.nextInt(input.length) + val bit = rand.nextInt(width) + if (!errorBits.contains((i, bit))) { + result(i) ^= BigInt(1) << bit + errorBits += ((i, bit)) + } + } + result + } +} diff --git a/src/test/scala/sideband/sidebandLinkNodeSerDesTest.scala b/src/test/scala/sideband/sidebandLinkNodeSerDesTest.scala index 3708e5ec..c7031323 100644 --- a/src/test/scala/sideband/sidebandLinkNodeSerDesTest.scala +++ b/src/test/scala/sideband/sidebandLinkNodeSerDesTest.scala @@ -90,6 +90,7 @@ class LinkSerDesTest extends AnyFlatSpec with ChiselScalatestTester { // Check serialized data c.io.in.valid.poke(false.B) + c.clock.step() for (i <- 0 until (c.msg_w / c.sb_w)) { val serialized_data = data((i + 1) * c.sb_w - 1, i * c.sb_w) c.io.in.ready.expect(false.B) @@ -99,6 +100,7 @@ class LinkSerDesTest extends AnyFlatSpec with ChiselScalatestTester { // make sure it does not take anything for 32 cycles for (i <- 0 until 32) { + println(i) c.io.in.ready.expect(false.B) c.clock.step() }