本文從本人的163博客搬遷至此。
MFM是改進型頻率調制的縮寫,其本質是一種非歸零碼,是用於磁介質硬盤存儲的一種調制方式。調制規則有兩句話,即兩個翻轉條件:
1、為1的碼元在每個碼元的正中進行一次翻轉;為0的碼元不翻轉。
2、對連續兩個為0的碼元,則在第一個為0的碼元結束時翻轉一次;單個的0碼元不翻轉。
設計過程:
若碼元的同步時鍾為CLK,不失一般性,假設CLK的上升沿開始產生新的碼元,下降沿為該碼元的正中。則MFM調制信號有可能在時鍾的上升沿,也有可能在時鍾的下降沿發生電平翻轉。由於一個觸發器不會在兩個邊沿都翻轉,也就不可能由單個觸發器的輸出作為MFM調制的輸出。一種合理的思路是分別由一個上升沿和一個下降沿觸發器分別在上升沿和下降沿翻轉,然后用它們的輸出相異或的結果作為總輸出。特別值得注意的是,異或具有如下的屬性:參與異或的兩個數,不論其中一個為0或1,只要另一個發生翻轉,異或的結果一定會翻轉。下面分別來實現MFM調制規則的兩個條件,即在滿足條件1時翻轉的電路——設其輸出為Dout1,和滿足條件2時翻轉的電路——設其輸出為Dout2,再將它們異或為最終的MFM調制結果Dout。
條件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 endmodule
上述代碼在Vivado中綜合后,得到下圖所示的Schematic。
圖2 在Vivado中綜合后產生的Schematic
這個實例再次印證了用硬件描述語言開發硬件電路的那個准則:在開始描述之前,腦中應該先有電路的大概模型,否則不可能綜合出滿足要求的硬件電路。