一個有趣的非同步時序邏輯電路設計例項 ——MFM調製模組設計筆記
本文從本人的163部落格搬遷至此。
MFM是改進型頻率調製的縮寫,其本質是一種非歸零碼,是用於磁介質硬碟儲存的一種調製方式。調製規則有兩句話,即兩個翻轉條件:
1、為1的碼元在每個碼元的正中進行一次翻轉;為0的碼元不翻轉。
2、對連續兩個為0的碼元,則在第一個為0的碼元結束時翻轉一次;單個的0碼元不翻轉。
設計過程:
若碼元的同步時鐘為CLK,不失一般性,假設CLK的上升沿開始產生新的碼元,下降沿為該碼元的正中。則MFM調製訊號有可能在時鐘的上升沿,也有可能在時鐘的下降沿發生電平翻轉。由於一個觸發器不會在兩個邊沿都翻轉,也就不可能由單個觸發器的輸出作為MFM調製的輸出。一種合理的思路是分別由一個上升沿和一個下降沿觸發器分別在上升沿和下降沿翻轉,然後用它們的輸出相異或的結果作為總輸出。特別值得注意的是,異或具有如下的屬性:參與異或的兩個數,不論其中一個為
條件1要求在為1的碼元的正中進行一次翻轉,比較容易實現,使用受控的觸發器時鐘即可。條件2較難實現,原因在於其對應的觸發器需要在第二個碼元為0的還沒有到來之前,先就對是否存在兩個連續為0的碼元做出判斷,並在第二個為0的碼元開始出現的上升沿就先翻轉。由於我們的電路不可能“未卜先知”地知道上升沿出現後的碼元是否為0,只能讓判斷條件2的電路延遲(潛伏)一個時鐘(CLK)週期後再發生翻轉。當然由於條件2翻轉的觸發器電路,需要延遲一個CLK,條件1翻轉的觸發器電路也必須隨之延遲一個時鐘週期,以同步於條件2翻轉的電路。圖1中的DinD就是延遲一個時鐘週期後的輸入被調製訊號。
圖1 MFM調製電路及其時序
滿足條件1的電路,應該由兩部分構成。第一個部分在上升沿動作,完成延遲一個時鐘週期。第二部分則在下降沿動作,當碼元為1時在下降沿翻轉Dout1,為一個T'觸發器。
滿足條件2的電路,也由兩部分組成。第一個部分是一組下降沿移位暫存器,該移位暫存器有兩個D觸發器構成,負責儲存最近兩個下降沿時刻碼元的值,當兩個觸發器同時為0時輸出允許Dout2翻轉的訊號。第二個部分是在第一部分允許的條件下,在上升沿翻轉的T'觸發器,其輸出就是Dout2。
從圖中可以看到,本電路是一個典型的非同步時序邏輯電路,為了在連續的一個/組下降沿和上升沿之間不間斷地動作,滿足條件1的電路和滿足條件2的電路的前後兩個部分所使用的時鐘邊沿都不相同。使用硬體描述語言時,需要分別使用兩個always模組來對應上升沿和下降沿電路。由於上升沿電路和下降沿電路交叉出現在兩個always模組中,這段VerilogHDL不太容易直接看懂。
1 module MFM( 2 3 input CLK, //產生被調製訊號的時鐘,其週期等於被調製訊號一個碼元的寬度 4 5 input Din, //被調製訊號 6 7 input rst, //復位訊號 8 9 output DinD, //延遲了一個clk週期的被調製訊號 10 11 output Dout1, //為1的碼元在碼元正中翻轉的訊號 12 13 output Dout2, //連續兩個為0的碼元,在兩個碼元之間翻轉的訊號 14 15 output MFM_Dout //Dout1和Dout2異或的結果,也就是DOUT1和Dout2翻轉時都翻轉的訊號。 16 17 ); 18 19 reg DinD_reg;//這個暫存器的值將輸入延遲了一個時鐘週期 20 21 reg Dout1_reg;//Dout1對應的暫存器 22 23 reg Dout2_reg;//Dout2對應的暫存器 24 25 reg[1:0] D_reg_n; //在下降沿緩衝兩級輸入,以判斷是否是連續兩個0 26 27 assign DinD = DinD_reg; 28 29 assign Dout1 = Dout1_reg; 30 31 assign Dout2 = Dout2_reg; 32 33 assign MFM_Dout = Dout1^Dout2; 34 35 //異或的屬性就是不論第一個自變數為0還是1,只要第二個自變數變化,結果都會跟著變化,因此MFM_Dout可以在DOUT1和Dout2翻轉時都翻轉 36 37 always @(posedge CLK or posedge rst) 38 39 begin 40 41 if(rst) 42 43 begin 44 45 DinD_reg <= 0; 46 47 Dout2_reg <= 0; 48 49 end 50 51 else begin 52 53 DinD_reg <= Din; 54 55 if(~(D_reg_n[0]|D_reg_n[1])) 56 57 //如果D_reg_n[0]和D_reg_n[1]都為0則翻轉Dout2 58 59 Dout2_reg <= ~Dout2_reg; 60 61 end 62 63 end 64 65 66 67 always @(negedge CLK or posedge rst) 68 69 begin 70 71 if(rst) 72 73 begin 74 75 Dout1_reg <= 0; 76 77 D_reg_n[1:0] <= 2'b11; 78 79 end 80 81 else begin 82 83 if (DinD == 1) 84 85 Dout1_reg <= ~Dout1_reg; 86 87 D_reg_n[1:0] <= {D_reg_n[0],Din}; 88 89 //緩衝兩個下降沿時的輸出,如果都為0,則需要在下一個上升沿翻轉Dout2 90 91 end 92 93 end 94 95 endmoduleView Code
上述程式碼在Vivado中綜合後,得到下圖所示的Schematic。
圖2 在Vivado中綜合後產生的Schematic
這個例項再次印證了用硬體描述語言開發硬體電路的那個準則:在開始描述之前,腦中應該先有電路的大概模型,否則不可能綜合出滿足要求的硬體電路。