串行/解串器的設計

 

 

 

 

PCIe總線是用來取代PCI總線的新型串行總線協議。這個與將要設計的串行解串器類似。對於利用串行解串器來進行數據傳輸的兩個系統,要發送的數據會先被保存下來,然後被送入到一個緩衝區內(即FIFO),接着,它們被串行化,然後一個一個比特地被髮送給目標系統。在接收端,輸入的串行數據被解串,然後再次保存到緩衝區內,並按照並行的格式被髮送到接收系統的並行總線上,爲了實現數據的同步,接收端必須從接收的串行數據中提取出數據的有效時鐘信息。這裏的時鐘其實和接收數據的幀邊界其實是同一個概念,整個串行/解串器的結構如圖所示:

SerDes原理圖

先從時鐘源設計開始:

一個典型的鎖相環(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.
//

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章