PCIe總線是用來取代PCI總線的新型串行總線協議。這個與將要設計的串行解串器類似。對於利用串行解串器來進行數據傳輸的兩個系統,要發送的數據會先被保存下來,然後被送入到一個緩衝區內(即FIFO),接着,它們被串行化,然後一個一個比特地被髮送給目標系統。在接收端,輸入的串行數據被解串,然後再次保存到緩衝區內,並按照並行的格式被髮送到接收系統的並行總線上,爲了實現數據的同步,接收端必須從接收的串行數據中提取出數據的有效時鐘信息。這裏的時鐘其實和接收數據的幀邊界其實是同一個概念,整個串行/解串器的結構如圖所示:
先從時鐘源設計開始:
一個典型的鎖相環(PLL)由以下三部分構成:輸出時鐘產生器,可變頻率振盪器(VFO)。PLL會比較輸入時鐘相位和VFO時鐘的差別,並根據這個差別來調整VFO產生的時鐘的頻率,其原理圖如下:
我們用目標庫中速度最快的延遲單元來完成 這個可以工作很高時鐘下的振盪器的設計,在verilog方面主要體現在以下兩方面:1.用generate來表示線延遲;2.添加DC的set_dont_touch來阻止綜合工具把我們認爲增加的線延遲給優化掉;
把這個振盪器命名爲FsatClock,並作爲時鐘驅動計數器,把計數器的門限值VaryFreq,用來控制VFO的時鐘頻率。
VFO的內部的比較器將計數值與計數器的溢出門限值VaryFreq相比較,當計數值達到門限值,計數器被複位。
以上我們可以得到一個32x的PLL,32xPLL受到1MHz的時鐘驅動,並且採樣VFO的PLL輸出。設置兩個計數器,一個是被外部的PLL1MHz的輸入時鐘驅動,另一個是被PLL內部的P1MHZ的溢出計數器產生的時鐘驅動,每個時鐘到來時把計數值進行比較,從而得到兩個時鐘的相對關係。只要兩個計數值不相等就會去調整VFO的頻率。這兩個2比特的計數器對32xPLL Muounter計數器產生時鐘輸出連續計數,在每一個PLL時鐘的上升沿,對應邊沿的計數值都用來做比較,從而產生樂調整VFO且及時更新的調整量。模塊Zeroer不斷檢測ClockIn的計數值,當計數值爲3時,計數器同時清零。
模塊的代碼如下:
1.Top
module PLLTop (output ClockOut, input ClockIn, Reset);
//
wire[1:0] AdjFreq;
wire MHz32, CtrCarry;
//
// -----------------------------------------------
// The new sample edge generator:
wire SampleWire;
//
DEL005 SampleDelay1 (.Z(SampleWire), .I(ClockIn));
//
//synopsys dc_tcl_script_begin
// set_dont_touch SampleDelay1
// set_dont_touch SampleWire
// set_dont_touch ClockIn
//synopsys dc_tcl_script_end
//
// -----------------------------------------------
assign ClockOut = MHz32;
//
ClockComparator
Comp1 ( .AdjustFreq(AdjFreq), .ClockIn(ClockIn)
, .CounterClock(CtrCarry), .Reset(Reset)
);
//
VFO
VFO1 ( .ClockOut(MHz32), .AdjustFreq(AdjFreq)
, .Sample(SampleWire), .Reset(Reset)
);
//
MultiCounter
MCntr1 (.CarryOut(CtrCarry), .Clock(MHz32), .Reset(Reset));
//
endmodule // PLLTop.
//
2.VFO
// The timescale and VFO_MaxDelta from the Deserializer
// design are used in VFO:
//
`include "SerDes.inc"
//
// ---------------------------------------------------
// The empirical frequency-setting parameters:
// DivideFactor is the number of fast-counter increments
// per PLL output clock. Thus, PLL output period is
// given by: T = 2*DivideFactor*ElemDelay, in which,
//
// ElemDelay = DelayElementAvgDelay
//
// ElemDelay is estimated in ns, averaged over rise &
// fall, and it is used in VCS message calculations as
// well as VFO frequency limit stops.
//
// ---------------------------------------------------
// The next macro configures the FastClock oscillator and
// FastDivvy counter (2 elems probably is too fast for
// DEL005 and 90-nm Typical gates):
//
`define NumElems 5 // Delay line length.
//
// ---------------------------------------------------
// We use the PLL multiplication factor, 32, to set the
// initial DivideFactor.
// We want to divide the frequency, and the delay line
// just gives edge delays, so the initial divide factor
// should be about 1/4 the PLL multiplication factor:
//
`define ElemDelay 0.0850 // Delay element avg. delay.
`define DivideFactor 32.0/(4.0*`NumElems*`ElemDelay)
//
// NFastBits will be calculated to ensure that the
// frequency initialization value (`DivideFactor) will
// be less than the maximum value of the FastDivvy and
// DivideFactor regs declared below.
// ---------------------------------------------------
module VFO (output ClockOut, input[1:0] AdjustFreq
, input Sample, Reset
);
reg ClockOutReg;
assign ClockOut = ClockOutReg;
//
// Configure the fast clock counter:
localparam NFastBits =
(`DivideFactor < (2**3 - (`VFO_MaxDelta/(2.0*`ElemDelay) + 1)) )? 3
: (`DivideFactor < (2**4 - (`VFO_MaxDelta/(2.0*`ElemDelay) + 1)) )? 4
: (`DivideFactor < (2**5 - (`VFO_MaxDelta/(2.0*`ElemDelay) + 1)) )? 5
: (`DivideFactor < (2**6 - (`VFO_MaxDelta/(2.0*`ElemDelay) + 1)) )? 6
: (`DivideFactor < (2**7 - (`VFO_MaxDelta/(2.0*`ElemDelay) + 1)) )? 7
: (`DivideFactor < (2**8 - (`VFO_MaxDelta/(2.0*`ElemDelay) + 1)) )? 8
: 9;
//
localparam[NFastBits-1:0] DivideLoLim = `DivideFactor - `VFO_MaxDelta;
localparam[NFastBits-1:0] DivideHiLim = `DivideFactor + `VFO_MaxDelta;
//
// Assertions:
`ifdef DC
`else
initial
begin
$display("VFO FastClock delay chain: %0.0f cells @%1.4f ns; f divider=[%0.0f] bits.\n"
, `NumElems, `ElemDelay, NFastBits
);
$display("VFO divide factor=%0.0f; so, initial SYNTH 1 MHz in => %2.1f MHz out.\n"
, `DivideFactor, 1000.0/32.0
);
$display("`VFO_MaxDelta=[%0.0f] => Divider limits: Low Lim=%0.0f and High Lim=%0.0f.\n"
, `VFO_MaxDelta, DivideLoLim, DivideHiLim
);
end
`endif
//
// ----------------------------------------------------------
// Generate the free-running clock (FastClock), using a chain
// of delay cells:
//
wire[`NumElems:0] WireD;
//
generate
genvar i;
for (i=0; i<`NumElems; i = i+1)
begin : DelayLine
DEL005 Delay85ps ( .Z(WireD[i+1]), .I(WireD[i]) );
end
endgenerate
//
//synopsys dc_tcl_script_begin
// set_dont_touch DelayLine*
// set_dont_touch *Delay85ps*
// set_dont_touch WireD*
//synopsys dc_tcl_script_end
//
reg FastClock;
//
always@(Reset, WireD)
begin : FastClockGen
if (Reset==1'b1)
FastClock = 1'b0;
else // The free-running clock gets the output of the delay line:
FastClock = WireD[`NumElems];
end
//
// Feed the inverted edge back into the delay line
// and create the oscillator inverter:
assign WireD[0] = ~FastClock;
//
// ----------------------------------------------------------
// Define the adjustable output clock. This is just a counter
// with a variable wrap to 0. FastDivvy wraps to set the delay
// according to the required DivideFactor.
//
reg[NFastBits-1:0] FastDivvy, DivideFactor;
//
always@(posedge FastClock, posedge Reset)
begin : ClockOutGen
if (Reset==1'b1)
begin
FastDivvy <= 'b0;
ClockOutReg <= 1'b0;
end
else begin
if (FastDivvy < DivideFactor)
FastDivvy <= FastDivvy + 1'b1;
else begin
FastDivvy <= 'b0;
ClockOutReg <= ~ClockOutReg;
end
end
end // ClockOutGen.
//
// ----------------------------------------------------------
// Use the PLL sampler to adjust the clock half-period.
//
always@(posedge Sample, posedge Reset)
begin : Sampler
if (Reset==1'b1)
DivideFactor <= `DivideFactor;
else begin
case (AdjustFreq)
2'b00: // Adjust f down (delay up):
if (DivideFactor < DivideHiLim)
DivideFactor <= DivideFactor + 1'b1;
2'b11: // Adjust f up (delay down):
if (DivideFactor > DivideLoLim)
DivideFactor <= DivideFactor - 1'b1;
endcase // Default: leave DivideFactor alone.
end
end // Sampler.
//
endmodule // VFO.
//
// Prevent local defines from getting to other
// compilation modules:
`undef ElemDelay
`undef NumElems
`undef DivideFactor
//
3.ClockCmp
module ClockComparator (output reg[1:0] AdjustFreq
, input ClockIn, CounterClock, Reset
);
reg[1:0] ClockInN, CounterClockN;
reg ZeroCounters;
//
always@(posedge ClockIn, posedge ZeroCounters)
begin : ClockCtr
if (ZeroCounters==1'b1)
ClockInN <= 2'b00;
else ClockInN <= ClockInN + 2'b01;
end
//
always@(posedge CounterClock, posedge ZeroCounters)
begin : CounterCtr
if (ZeroCounters==1'b1)
CounterClockN <= 2'b00;
else CounterClockN <= CounterClockN + 2'b01;
end
//
always@(posedge ClockIn, posedge Reset)
begin : Zeroer
if (Reset==1'b1)
ZeroCounters <= 1'b1;
else ZeroCounters <= (ClockInN==2'b11)? 1'b1 : 1'b0;
end
//
// Clock with the sampled CounterClock so that
// ClockIn is set up for case statement:
//
always@(posedge CounterClock, posedge Reset)
begin : EdgeComparator
if (Reset==1'b1) AdjustFreq = 2'b01;
else
case (ClockInN)
2'b00: begin
case (CounterClockN)
2'b00: AdjustFreq = 2'b01; // No change.
2'b01: AdjustFreq = 2'b01; // No change.
2'b10: AdjustFreq = 2'b00; // Slow the counter.
default: AdjustFreq = 2'b00; // Slow the counter.
endcase
end
2'b01: begin
case (CounterClockN)
2'b00: AdjustFreq = 2'b11; // Speed up the counter.
2'b01: AdjustFreq = 2'b01; // No change.
2'b10: AdjustFreq = 2'b00; // Slow the counter.
default: AdjustFreq = 2'b00; // Slow the counter.
endcase
end
2'b10: begin
case (CounterClockN)
2'b00: AdjustFreq = 2'b11; // Speed up the counter.
2'b01: AdjustFreq = 2'b11; // Speed up the counter.
2'b10: AdjustFreq = 2'b10; // No change.
default: AdjustFreq = 2'b00; // Slow the counter.
endcase
end
default: begin // Includes 2'b11; allows initialization:
case (CounterClockN)
2'b00: AdjustFreq = 2'b11; // Speed up the counter.
2'b01: AdjustFreq = 2'b11; // Speed up the counter.
2'b10: AdjustFreq = 2'b10; // No change.
default: AdjustFreq = 2'b10; // No change.
endcase
end
endcase
end
//
endmodule // ClockComparator.
在Deserializer部分,包括FIFO緩衝器,串行解碼器以及串行接收模塊:
1.FIFO部分\
FIFO的狀態轉移如圖所示:
FIFO由存儲器和寄存器的控制模塊構成。
存儲器:
我們需要同時讀寫memory,所以需要兩條總線,把讀寫邏輯分成兩個always中去,其中Reader塊要進行奇偶校驗,因此,奇偶校驗錯誤時,輸出應該被複位成0. 而Write塊需要初始化memory,即是給所有的地址都寫.
module DPMem1kx32
#(parameter AWid = 5 // Default length of RAM storage.
, DWid = 32 // Default addressable word width.
)
( output Dready // Data valid during a read.
, ParityErr // Parity error on read.
, output[DWid-1:0] DataO // For read from storage.
, input[DWid-1:0] DataI // For write to storage.
, input[AWid-1:0] AddrR // Read address.
, input[AWid-1:0] AddrW // Write address.
, input ClkR // Clocks data out.
, input ClkW // Clocks data in.
, ChipEna // Enables data output drivers.
, Read // Stored data are copied to Data[].
, Write // Data[] values replace stored values.
, Reset // Clears memory to allow parity checks.
);
//
localparam MemHi = (1<<AWid) - 1;
//
reg[DWid:0] Storage[MemHi:0]; // RAM storage (internal);
// bit 32 is parity.
reg[DWid-1:0] DataOr; // Holds data output.
wire ClockR, ClockW; // Gated by ChipEna.
reg Parityr // To drive output pin.
, Dreadyr; // To drive output pin.
//
integer i;
//
// Prevent any clocking if not ChipEna:
assign ClockR = (ChipEna==1'b1)? ClkR : 1'b0;
assign ClockW = (ChipEna==1'b1)? ClkW : 1'b0;
//
// Connect to output pins:
assign ParityErr = Parityr;
assign Dready = (ChipEna==1'b1)? Dreadyr : 1'b0;
assign DataO = (ChipEna==1'b1)? DataOr : 'bz;
//
//`define DEBUG
//
always@(posedge ClockR, posedge Reset)
begin : Reader
if (Reset==1'b1)
begin
Parityr <= 1'b0;
Dreadyr <= 1'b0;
DataOr <= 'b0;
end
else if (Read==1'b1)
begin
// First, parity check:
Parityr <= ^(Storage[AddrR]);
// Then, read out data, even if bad:
`ifdef DEBUG
$display("%05d: Read Storage[%02h]=[%08h]."
, $time, AddrR, Storage[AddrR]);
`endif
DataOr <= Storage[AddrR]; // Lower 32 bits will go.
Dreadyr <= 1'b1; // Flag valid data.
`ifdef DC
`else
if (Parityr==1'b1)
$display(" *** *** Parity Error. time=%4d", $time);
`endif
end
end // Reader.
//
always@(posedge ClockW, posedge Reset)
begin : Writer
if (Reset==1'b1)
for (i=0; i<=MemHi; i=i+1) Storage[i] <= 'b0;
else
if (Write==1'b1)
begin
`ifdef DEBUG
$display("%05d: Write Storage[%02h]=[%08h]."
, $time, AddrW, Storage[AddrW]);
`endif
Storage[AddrW] <= {^DataI[DWid-1:0], DataI[DWid-1:0]};
end
end // Writer.
endmodule // DPMem1kx32.
//
2.FIFO狀態機
因爲兩套總線,兩套時鐘信號:除非只有一個時鐘,否則狀態機的狀態時不確定的,所以需要利用這些輸入來產生一個時鐘。
always@(ClkR, ClkW)
StateClockRaw = ~(ClkR && ClkW);
狀態機必須保存狀態,因此需要一個時鐘作用下的時序邏輯狀態轉移模塊,把我們之前的約束用於編碼,要把非阻塞賦值和阻塞賦值分開,因此:
1.在一個小的時鐘觸發模塊裏使用非阻塞賦值語句實現狀態轉移,
2.FIFO的寄存器放在另外一個模塊裏面。
3.我們還需要兩個時序元件:讀計數器和寫計數器
對於讀寫的計數器:我們需要實現當輸入爲1'b1時,地址就會增加的,當輸入爲1'b0時,地址就會停止的相關操作;
對於讀指針來說,其跳轉的always應該這麼寫:
// --------------------------------------------------------
// Read logic:
//
always@(posedge StateClock, posedge Reset)
begin : IncrReadBlock
if (Reset==1'b1)
ReadAr <= 'b0;
else begin
if (CurState==emptyS)
ReadAr <= 'b0;
else if (ReadCmdr==1'b1)
ReadAr <= ReadAr + 1;
end
end
//
通過控制IncrRead任務輸入了,來控制ReadCmdr的輸出,從而控制讀指針的跳轉;
如果FIFO是空的,照理說,往哪裏寫數據都是可以的,但是emptyS裏的ReadAr的初始值我們可以不用去關心,但是我們希望計數器從一個確定的值開始增加。兩個計數器從一個值同時增加
因此讀任務可以這麼寫:
task incrRead(input ActionR);
begin
if (ActionR==1'b1)
begin
if (CurState==emptyS)
begin
ReadCmdr = 1'b0;
OldReadAr = 'b0;
end
else begin
if (OldReadAr==ReadAr) // Schedule an incr.
ReadCmdr = 1'b1;
else begin // No incr; already changed:
ReadCmdr = 1'b0;
OldReadAr = ReadAr;
end
end
end
else begin // ActionR is a reset:
ReadCmdr = 1'b0;
OldReadAr = 'b0;
end
end
endtask
同理還有寫指針的跳轉邏輯:
// Write logic:
//
always@(posedge StateClock, posedge Reset)
begin : IncrWriteBlock
if (Reset==1'b1)
WriteAr <= 'b0;
else begin
case (CurState)
emptyS: WriteAr <= 'b0; // Set equal to emptyS read addr.
fullS: WriteAr <= ReadAr; // Set equal to first valid addr.
endcase // No default.
// If current state not special:
if (CurState!=fullS && WriteCmdr==1'b1)
WriteAr <= WriteAr + 1;
end
end
//
task incrWrite(input ActionW);
begin
if (ActionW==1'b1)
begin
if (CurState==fullS)
begin
WriteCmdr = 1'b0;
OldWriteAr = ReadAr;
end
else begin
if (OldWriteAr==WriteAr) // Schedule an incr.
WriteCmdr = 1'b1;
else begin // No incr; already changed:
WriteCmdr = 1'b0;
OldWriteAr = WriteAr;
end
end
end
else begin // ActionW is a reset:
WriteCmdr = 1'b0;
OldWriteAr = 'b0;
end
end
endtask
最後是狀態機的狀態轉移邏輯,按照泡泡圖的轉移邏輯編寫:
always@(negedge StateClock, posedge Reset)
begin
if (Reset==1'b1)
begin
// Reset conditions:
NextState = emptyS;
incrRead(1'b0); // 0 -> reset counter.
incrWrite(1'b0); // 0 -> reset counter.
end
else
case (CurState)
// ---------------------------
emptyS:// This state combines the hardware reset
// with a simple empty state during operation:
begin
EmptyFIFOr = 1'b1;
FullFIFOr = 1'b0;
ReadCmdr = 1'b0; // Just in case.
// One transition rule:
if (WriteReq==1'b1)
begin
incrWrite(1'b1);
EmptyFIFOr = 1'b0;
NextState = a_emptyS;
end
else WriteCmdr = 1'b0; // Just in case.
end
// ---------------------------
a_emptyS:// In this state, we know W == R+1; one read at
// the current read address will invalidate the
// last datum and leave the read pointer nowhere.
// However, one write will put the FIFO into
// normalS state:
begin
EmptyFIFOr = 1'b0;
// Memory control logic:
case ({ReadReq,WriteReq})
2'b01: begin
incrWrite(1'b1);
ReadCmdr = 1'b0;
end
2'b10: begin
incrRead(1'b1);
WriteCmdr = 1'b0;
end
2'b11: begin // Concurrent calls:
incrRead(1'b1);
incrWrite(1'b1);
end
default: begin // No request pending:
ReadCmdr = 1'b0;
WriteCmdr = 1'b0;
end
endcase
//
// Transition logic:
// Set default:
NextState = a_emptyS;
//
tempAr = ReadAr + 2;
if (WriteAr==tempAr)
NextState = normalS;
else if (WriteAr==ReadAr)
NextState = emptyS;
//
end // a_emptyS state.
// ---------------------------
normalS:// In this state, we know W > R+1
// and R > W+1 before anything happens here.
// The simplest way to look for a transition,
// then, is just to compare the new counter
// value + 1 with the other counter value.
// If, after increment, W == R+1 or R == W+1,
// we are no longer in the normalS state.
begin
// Memory control logic:
case ({ReadReq,WriteReq})
2'b01: begin
incrWrite(1'b1);
ReadCmdr = 1'b0;
end
2'b10: begin
incrRead(1'b1);
WriteCmdr = 1'b0;
end
2'b11: begin // Concurrent calls:
incrRead(1'b1);
incrWrite(1'b1);
end
default: begin // No request pending:
ReadCmdr = 1'b0;
WriteCmdr = 1'b0;
end
endcase
//
// Transition logic:
// Default is normalS:
NextState = normalS;
//
// Check for a_fullS:
tempAr = WriteAr+1;
if (ReadAr==tempAr) NextState = a_fullS;
//
// Check for a_emptyS:
tempAr = ReadAr+1;
if (tempAr==WriteAr) NextState = a_emptyS;
//
end // normalS state.
// ---------------------------
a_fullS:// In this state, we know R == W+1; one write at
// the current write address will invalidate the
// last datum and leave the write pointer nowhere.
// However, one read will put the FIFO into
// normalS state:
begin
FullFIFOr = 1'b0;
// Memory control logic:
case ({ReadReq,WriteReq})
2'b01: begin
incrWrite(1'b1);
ReadCmdr = 1'b0;
end
2'b10: begin
incrRead(1'b1);
WriteCmdr = 1'b0;
end
2'b11: begin // Concurrent calls:
incrRead(1'b1);
incrWrite(1'b1);
end
default: begin // No request pending:
ReadCmdr = 1'b0;
WriteCmdr = 1'b0;
end
endcase
//
// Transition logic:
// Set default:
NextState = a_fullS;
//
tempAr = WriteAr + 2;
if (ReadAr==tempAr)
NextState = normalS;
else if (ReadAr==WriteAr)
NextState = fullS;
//
end // a_fullS state.
// ---------------------------
fullS:// This state has just one possible transition,
// on a read request:
begin
FullFIFOr = 1'b1;
WriteCmdr = 1'b0; // Just in case.
if (ReadReq==1'b1)
begin
incrRead(1'b1);
FullFIFOr = 1'b0;
NextState = a_fullS;
end
else ReadCmdr = 1'b0; // Just in case.
end // fullS state.
// ---------------------------
default: NextState = emptyS; // Always handle the unknown.
endcase
end // always@(*)
串並解碼部分
1.完成移位,用一個函數來完成這個功能。這個函數每隔一段延遲時間就進行一次移位操作。
always@(negedge SerClock, posedge Reset)
begin : Shift1
// Respond to external reset:
if (Reset==YES)
FrameSR <= 'b0;
else begin
FrameSR <= FrameSR<<1;
FrameSR[0] <= SerIn;
end
end
2.轉換,將移位寄存器的值放到並行輸出的數據總線上。隔一些延遲後,輸出一個ParValid有效標誌
// Unload32 block: Copies the contents of the 32-bit
// Decoder register onto the 32-bit output bus ParOut.
//
always@(negedge SerClock, posedge Reset)
begin : Unload32
if (Reset==YES)
begin
ParValidr <= NO; // Lower the flag.
ParOutr <= 'b0; // Zero the output.
ParValidTimer <= 'b0;
end
else begin
if (UnLoad==YES)
begin
ParOutr <= Decoder; // Move the data.
ParValidr <= YES; // Set the flag.
ParValidTimer <= 'b0;
end
else begin
if (ParValidTimer<ParValidMinCnt)
ParValidTimer <= ParValidTimer + 1;
else if (ParClk==1'b0)
ParValidr <= NO; // Terminates assertion.
end
end // UnloadParData.
end
//
解串譯碼器的功能,它能在輸入的串行數據流中找到幀邊界。同時,它還恢復了和發送端同步的1MHz的時鐘,在我們的設計中,每一幀有16比特,並且以8比特作爲幀的邊界。DesDecoder會從數據流中找到有效的數據位,解串器會橫跨兩個不同的時鐘閾,串行數據的輸入速率只和發送端的時鐘頻率有關,而解串器使用接收端的時鐘將把FIFO中的串行數據讀出來。因此,這個FIFO的輸入是發送時鐘閾的,而FIFO的輸出是接收時鐘閾的。
再來看Deserializer的PLL,它產生了一個頻率爲1MHz的時鐘。desdecoder希望這個時鐘和發送端的時鐘同步。產生這個時鐘的過程類似於一個壓腔振盪器的工作過程,輸出的時鐘頻率只和解串的操作和輸入的數據有關,PLL自己產生的1MHz的時鐘,這個時鐘被32倍頻後用來接收串行的數據,二,是DesDecoder從串行數裏恢復出來的時鐘。如果這兩個時鐘可以同步那麼發送端發送過來的數據可以被正確地解析。
判定同步:
當數據8‘b000_00_000被送入SerIn之後,直到四組邊界值移進了移位寄存器之後才認爲是同步成功了。
失步判決:
如果兩個比特的連續計數器的數值不再連續,或者移位的值填不滿64比特,我們認爲都是失步的;
// Decode4 block: Does nothing unless it finds a PAD0
// pattern in the low-order byte of the SR.
//
// To the 32-bit Decoder; requests copy to ParOut.
// Detects packet alignment;
// determines synchronized or desynchronized state.
// requests sync of ParClk upon resynch.
//
// Called on every SerClock, after a new bit has been
// shifted in.
//
/* The numbers help identify bits in the shift register vector:
* 60 50 40 30 20 10 0
*32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210
*xxxxxxxx_00011000_xxxxxxxx_00010000_xxxxxxxx_00001000_xxxxxxxx_00000000;
* PAD3 PAD2 PAD1 PAD0
* 8'18 8'10 8'08 8'00
*/
// outputs: Decoder, doParSync, SyncOK, UnLoad.
// inputs: FrameSR, SerClock, ParClk, Reset I/O.
//
`ifdef DC
`else
reg[9:0] PacketN;
initial PacketN = 'b0;
`endif
always@(negedge SerClock, posedge Reset)
begin : Decode4
if (Reset==YES)
begin
Decoder <= 'b0;
doParSync <= NO;
SyncOK <= NO;
UnLoad <= NO;
end
else begin : PacketFind // Look for packet alignment:
UnLoad <= NO;
doParSync <= NO;
if ( FrameSR[7:0]==PAD0 )
begin : FoundPAD0
SyncOK <= YES;
if ( FrameSR[23:16]==PAD1 && FrameSR[39:32]==PAD2 && FrameSR[55:48]==PAD3 )
begin // If all pads indicate all frames aligned:
`ifdef DC
`else
PacketN = PacketN + 1;
$display("DesDecoder: Packet at t=%07d. N=%d", $time, PacketN);
`endif
Decoder <= { FrameSR[63:56], FrameSR[47:40], FrameSR[31:24], FrameSR[15:8] };
UnLoad <= YES;
end
else // Found a PAD0, but rest failed; so, synchronize:
begin
doParSync <= YES;
SyncOK <= NO;
end // If all pads found.
end // FoundPAD0
end // PacketFind.
end
//
desdecoder從串行數據中提取出1MhZ的信號,並用它作爲並行3MHZ的串行時鐘即可。端口SerIn接串行的數據;SerClk接收串行的輸入時鐘;ParClk是從串行數據中提取出來的輸出時鐘,ParBus是輸出的32比特總線,受ParClk驅動和:
串行編碼:
從串行器的FIFO讀數據,併發送串行的數據包
module SerEncoder
#(parameter DWid = 32)
( output SerOut, SerValid, FIFO_ReadReq
, input[DWid-1:0] ParIn
, input F_Empty, ParClk, SerClk, ParValid, Reset
);
//
// These derived params are an incomplete effort to
// make the design configurable by passed params,
// only:
// First, calculate the Log2 of the word (data) width:
// Assumes width <= 256:
localparam Log2DWid = (DWid == 1)? 0
: (DWid == 2)? 1
: (DWid <= 4)? 2
: (DWid <= 8)? 3
: (DWid <= 16)? 4
: (DWid <= 32)? 5
: (DWid <= 64)? 6
: (DWid <= 128)? 7
: (DWid <= 256)? 8
: 9;
//
localparam Log2ShN = 1; // 1 -> 2 words = 64 bits.
localparam ShCtrW = Log2DWid+Log2ShN; // Shifter counter width.
//
// The framing pad localparam formats:
`include "SerDesFormats.inc"
//
// --------------------------------------------------
// Declarations:
//
reg[DWid-1:0] InBuf; // The data input buffer.
reg[ShCtrW-1:0] Sh_N; // Shifter bit counter.
//
reg SerOutr, SerValidr, HalfParClkr;
//
assign SerOut = SerOutr;
assign SerValid = SerValidr;
//
// --------------------------------------------------
// FIFOReader "block". Asserts the FIFO_ReadReq to
// make new FIFO input data available for
// serialization:
//
assign FIFO_ReadReq
= HalfParClkr && !F_Empty && ParValid && !Reset;
//
// --------------------------------------------------
// Half-freq clock for data read:
//
always@(posedge ParClk, posedge Reset)
if (Reset==1'b1)
HalfParClkr <= 1'b0;
else HalfParClkr <= ~HalfParClkr; // => 1/2 MHz.
//
// --------------------------------------------------
// Shifter block: Called on posedge SerClk.
// Controls the shift register. Uses the shift bit
// count in Sh_N to shift onto the serial output port
// either a new data bit from the InBuf or a pad
// bit.
//
// The Sh_N sequence for any packet is as follows,
// with higher bits first:
// 63 - 56 InBuf[24:31]
// 55 - 48 PAD3
// 47 - 40 InBuf[16:23]
// 39 - 32 PAD2
// 31 - 24 InBuf[08:15]
// 23 - 16 PAD1
// 15 - 08 InBuf[00:07]
// 07 - 00 PAD0
//
// Outputs: SerOut.
// Inputs: InBuf, PADx, SerClk, Reset.
//
always@(posedge SerClk, posedge Reset)
begin : ShifterBlock
if (Reset==YES)
begin
Sh_N <= 'b0;
SerOutr <= 1'b0;
end
else begin
Sh_N <= Sh_N - 6'h1;
//
if (Sh_N<=07) SerOutr <= PAD0[Sh_N]; // --> 0 - 7.
else
if (Sh_N<=15) SerOutr <= InBuf[Sh_N-08]; // --> 0 - 7.
else
if (Sh_N<=23) SerOutr <= PAD1[Sh_N-16]; // --> 0 - 7.
else
if (Sh_N<=31) SerOutr <= InBuf[Sh_N-16]; // --> 8 - 15.
else
if (Sh_N<=39) SerOutr <= PAD2[Sh_N-32]; // --> 0 - 7.
else
if (Sh_N<=47) SerOutr <= InBuf[Sh_N-24]; // --> 16 - 23.
else
if (Sh_N<=55) SerOutr <= PAD3[Sh_N-48]; // --> 0 - 7.
else
if (Sh_N<=63) SerOutr <= InBuf[Sh_N-32]; // --> 24 - 31.
`ifdef DC
`else
else
begin
$display("time=%t: SerEncoder Shifter illegal count=[%02d]\nin %m."
, $time, Sh_N
);
#100 $stop;
end
`endif
end // if not Reset.
end // always Shifter.
//
// --------------------------------------------------
// Loader block: Called on posedge HalfParClkr.
//
// Outputs: InBuf, SerValid.
// Inputs: F_Empty, ParIn, HalfParClkr, ParValid, Reset.
//
always@(posedge HalfParClkr, posedge Reset)
begin : LoaderBlock
begin
if (Reset==YES)
begin
InBuf <= 'b0;
SerValidr <= NO;
end
else begin
if (F_Empty==NO && ParValid==YES)
begin
InBuf <= ParIn;
SerValidr <= YES;
end
else begin
InBuf <= 'b0;
SerValidr <= NO;
end
end
end
end // always Loader.
//
endmodule // SerEncoder.
//