筆試時(shí)也很常見。
[例1] 一個(gè)簡(jiǎn)單的狀態(tài)機(jī)設(shè)計(jì)--序列檢測(cè)器
序列檢測(cè)器是時(shí)序數(shù)字電路設(shè)計(jì)中經(jīng)典的教學(xué)范例,下面我們將用Verilog HDL語言來描述、仿真、并實(shí)現(xiàn)它。
序列檢測(cè)器的邏輯功能描述:
序列檢測(cè)指的就是將一個(gè)指定的序列從數(shù)字碼流中識(shí)別出來。本例中,我們將設(shè)計(jì)一個(gè)“10010”序列的檢測(cè)器。設(shè)X為數(shù)字碼流輸入,Z為檢出標(biāo)記輸出,高電平表示“發(fā)現(xiàn)指定序列”,低電平表示“沒有發(fā)現(xiàn)指定序列”??紤]碼流為“ 110010010000100101…”
在時(shí)鐘2-6,碼流X中出現(xiàn)指定序列“10010”,對(duì)應(yīng)輸出Z在第6個(gè)時(shí)鐘變?yōu)楦唠娖建D―“1”,表示“發(fā)現(xiàn)指定序列”。同樣地,在時(shí)鐘13-17碼流,X中再次出現(xiàn)指定序列“10010”,Z輸出“1”。注意,在時(shí)鐘5-9還有一次檢出,但它是與第一次檢出的序列重疊的,即前者的前面兩位同時(shí)也是后者的最后兩位。
根據(jù)以上邏輯功能描述,我們可以分析得出狀態(tài)轉(zhuǎn)換圖如下:
其中狀態(tài)A-E表示5比特序列“10010”按順序正確地出現(xiàn)在碼流中。考慮到序列重疊的可能,轉(zhuǎn)換圖中還有狀態(tài)F、G。另外、電路的初始狀態(tài)設(shè)為IDLE。
進(jìn)一步,我們得出Verilog HDL代碼。
//文件:sequence.v
module seqdet( x, z, clk, rst);
input x,clk, rst;
output z;
reg [2:0] state;//狀態(tài)寄存器
wire z;
parameter IDLE= ‘d0, A=’d1, B=‘d2,
C=’d3, D=‘d4,
E=’d5, F=‘d6,
G=’d7;
assign z=(state==D && x==0) ? 1 :0;
always @(posedge clk or negedge rst)
if(!rst)
begin
state《=IDLE;
end
else
casex( state)
IDLE: if(x==1)
begin
state《=A;
end
A: if (x==0)
begin
state《=B;
end
B: if (x==0)
begin
state《=C;
end
else
begin
state《=F;
end
C: if(x==1)
begin
state《=D;
end
else
begin
state《=G;
end
D: if(x==0)
begin
state《=E;
end
else
begin
state《=A;
end
E: if(x==0)
begin
state《=C;
end
else
begin
state《=A;
end
F: if(x==1)
begin
state《=A;
end
else
begin
state《=B;
end
G: if(x==1)
begin
state《=F;
end
default: state《=IDLE;
endcase
endmodule
為了驗(yàn)證其正確性,我們接著編寫測(cè)試用代碼。
//文件:sequence.tf
`timescale 1ns/1ns
module t;
reg clk, rst;
reg [23:0] data;
wire z,x;
assign x=data[23];
initial
begin
clk《=0;
rst《=1;
#2 rst《=0;
#30 rst《=1; //復(fù)位信號(hào)
data=‘b1100_1001_0000_1001_0100; //碼流數(shù)據(jù)
end
always #10 clk=~clk; //時(shí)鐘信號(hào)
always @ (posedge clk) // 移位輸出碼流
data={data[22:0],data[23]};
seqdet m ( .x(x), .z(z), .clk(clk), .rst(rst)); //調(diào)用序列檢測(cè)器模塊
// Enter fixture code here
endmodule // t
其中、X碼流的產(chǎn)生,我們采用了移位寄存器的方式,以方便更改測(cè)試數(shù)據(jù)。仿真結(jié)果如下圖所示:
從波形中,我們可以看到程序代碼正確地完成了所要設(shè)計(jì)的邏輯功能。另外,sequence.v的編寫,采用了可綜合的Verilog HDL 風(fēng)格,它可以通過綜合器的綜合最終實(shí)現(xiàn)到FPGA中。
說明:以上編程、仿真、綜合和后仿真在PC WINDOWS NT 4.0操作系統(tǒng)及QuickLogic SPDE環(huán)境下通過。
[例2]EEPROM讀寫器件的設(shè)計(jì)
下面我們將介紹一個(gè)經(jīng)過實(shí)際運(yùn)行驗(yàn)證并可綜合到各種FPGA和ASIC工藝的串行EEPROM讀寫器件的設(shè)計(jì)過程。列出了所有有關(guān)的Verilog HDL程序。這個(gè)器件能把并行數(shù)據(jù)和地址信號(hào)轉(zhuǎn)變?yōu)榇蠩EPROM能識(shí)別的串行碼并把數(shù)據(jù)寫入相應(yīng)的地址,或根據(jù)并行的地址信號(hào)從EEPROM相應(yīng)的地址讀取數(shù)據(jù)并把相應(yīng)的串行碼轉(zhuǎn)換成并行的數(shù)據(jù)放到并行地址總線上。當(dāng)然還需要有相應(yīng)的讀信號(hào)或?qū)懶盘?hào)和應(yīng)答信號(hào)配合才能完成以上的操作。
1.二線制I2C CMOS 串行EEPROM的簡(jiǎn)單介紹二線制I2C CMOS 串行EEPROM AT24C02/4/8/16 是一種采用CMOS 工藝制成的串行可用電擦除可編程只讀存儲(chǔ)器。串行EEPROM 一般具有兩種寫入方式,一種是字節(jié)寫入方式,還有另一種頁寫入方式,允許在一個(gè)寫周期內(nèi)同時(shí)對(duì)一個(gè)字節(jié)到一頁的若干字節(jié)進(jìn)行編程寫入,一頁的大小取決于芯片內(nèi)頁寄存器的大小,不同公司的同一種型號(hào)存儲(chǔ)器的內(nèi)頁寄存器可能是不一樣的。
為了程序的簡(jiǎn)單起見,在這里只編寫串行 EEPROM 的一個(gè)字節(jié)的寫入和讀出方式的Verilog HDL 的行為模型代碼,串行EEPROM讀寫器的Verilog HDL模型也只是字節(jié)讀寫方式的可綜合模型,對(duì)于頁寫入和讀出方式,讀者可以參考有關(guān)書籍,改寫串行EEPROM 的行為模型和串行EEPROM讀寫器的可綜合模型。
2.I2C (Inter Integrated Circuit)總線特征介紹I2C 雙向二線制串行總線協(xié)議定義如下:
只有在總線處于“非忙”狀態(tài)時(shí),數(shù)據(jù)傳輸才能被初始化。在數(shù)據(jù)傳輸期間,只要時(shí)鐘線為高電平,數(shù)據(jù)線都必須保持穩(wěn)定,否則數(shù)據(jù)線上的任何變化都被當(dāng)作“啟動(dòng)”或“停止”信號(hào)。
①總線非忙狀態(tài)(A 段)
數(shù)據(jù)線SDA 和 時(shí)鐘線 SCL 都保持高電平。
②啟動(dòng)數(shù)據(jù)傳輸(B 段)
當(dāng)時(shí)鐘線(SCL)為高電平狀態(tài)時(shí),數(shù)據(jù)線(SDA)由高電平變?yōu)榈碗娖降南陆笛乇徽J(rèn)為是“啟動(dòng)”信號(hào)。只有出現(xiàn)“啟動(dòng)”信號(hào)后,其它的命令才有效。
③停止數(shù)據(jù)傳輸(C 段)
當(dāng)時(shí)鐘線(SCL)為高電平狀態(tài)時(shí),數(shù)據(jù)線(SDA)由低電平變?yōu)楦唠娖降纳仙乇徽J(rèn)為是“停止”信號(hào)。隨著“停在”信號(hào)出現(xiàn),所有的外部操作都結(jié)束。
④數(shù)據(jù)有效(D 段)
在出現(xiàn)“啟動(dòng)”信號(hào)以后,在時(shí)鐘線(SCL)為高電平狀態(tài)時(shí)數(shù)據(jù)線是穩(wěn)定的,這時(shí)數(shù)據(jù)線的狀態(tài)就要傳送的數(shù)據(jù)。數(shù)據(jù)線(SDA)上的數(shù)據(jù)的改變必須在時(shí)鐘線為低電平期間完成,每位數(shù)據(jù)占用一個(gè)時(shí)鐘脈沖。每個(gè)數(shù)傳輸都是由“啟動(dòng)”信號(hào)開始,結(jié)束于“停止”信號(hào)。
⑤應(yīng)答信號(hào)
每個(gè)正在接收數(shù)據(jù)的EEPROM 在接到一個(gè)字節(jié)的數(shù)據(jù)后,通常需要發(fā)出一個(gè)應(yīng)答信號(hào)。而每個(gè)正在發(fā)送數(shù)據(jù)的EEPROM 在發(fā)出一個(gè)字節(jié)的數(shù)據(jù)后,通常需要接收一個(gè)應(yīng)答信號(hào)。EEPROM 讀寫控制器必須產(chǎn)生一個(gè)與這個(gè)應(yīng)答位相聯(lián)系的額外的時(shí)鐘脈沖。在EEPROM 的讀操作中,EEPROM 讀寫控制器對(duì)EEPROM 完成的最后一個(gè)字節(jié)不產(chǎn)生應(yīng)答位,但是應(yīng)該給EEPROM 一個(gè)結(jié)束信號(hào)。
3.二線制I2C CMOS 串行EEPROM讀寫操作1) EEPROM 的寫操作(字節(jié)編程方式)所謂EEPROM的寫操作(字節(jié)編程方式)就是通過讀寫控制器把一個(gè)字節(jié)數(shù)據(jù)發(fā)送到EEPROM 中指定地址的存儲(chǔ)單元。
其過程如下:EEPROM 讀寫控制器發(fā)出“啟動(dòng)”信號(hào)后,緊跟著送4位I2C總線器件特征編碼1010 和3 位EEPROM 芯片地址/頁地址XXX 以及寫狀態(tài)的R/W 位(=0),到總線上。這一字節(jié)表示在接收到被尋址的EEPROM 產(chǎn)生的一個(gè)應(yīng)答位后,讀寫控制器將跟著發(fā)送1個(gè)字節(jié)的EEPROM 存儲(chǔ)單元地址和要寫入的1個(gè)字節(jié)數(shù)據(jù)。
EEPROM 在接收到存儲(chǔ)單元地址后又一次產(chǎn)生應(yīng)答位以后,讀寫控制器才發(fā)送數(shù)據(jù)字節(jié),并把數(shù)據(jù)寫入被尋址的存儲(chǔ)單元。EEPROM 再一次發(fā)出應(yīng)答信號(hào),讀寫控制器收到此應(yīng)答信號(hào)后,便產(chǎn)生“停止”信號(hào)。字節(jié)寫入幀格式如圖2所示:
2)二線制I2C CMOS 串行EEPROM 的讀操作所謂EEPROM的讀操作即通過讀寫控制器讀取 EEPROM 中指定地址的存儲(chǔ)單元中的一個(gè)字節(jié)數(shù)據(jù)。串行EEPROM 的讀操作分兩步進(jìn)行:讀寫器首先發(fā)送一個(gè)“啟動(dòng)”信號(hào)和控制字節(jié)(包括頁面地址和寫控制位)到EEPROM,再通過寫操作設(shè)置EEPROM 存儲(chǔ)單元地址(注意:雖然這是讀操作,但需要先寫入地址指針的值),在此期間EEPROM 會(huì)產(chǎn)生必要的應(yīng)答位。
接著讀寫器重新發(fā)送另一個(gè)“啟動(dòng)”信號(hào)和控制字節(jié)(包括頁面地址和讀控制位R/W = 1),EEPROM收到后發(fā)出應(yīng)答信號(hào),然后,要尋址存儲(chǔ)單元的數(shù)據(jù)就從SDA 線上輸出。讀操作有三種:讀當(dāng)前地址存儲(chǔ)單元的數(shù)據(jù)、讀指定地址存儲(chǔ)單元的數(shù)據(jù)、讀連續(xù)存儲(chǔ)單元的數(shù)據(jù)。在這里只介紹讀指定地址存儲(chǔ)單元數(shù)據(jù)的操作。
4.EEPROM的Verilog HDL 程序要設(shè)計(jì)一個(gè)串行EEPROM讀寫器件,不僅要編寫EEPROM讀寫器件的可綜合Verilog HDl的代碼,而且要編寫相應(yīng)的測(cè)試代碼以及EERPOM的行為模型。
1) EEPROM的行為模型為了設(shè)計(jì)這樣一個(gè)電路我們首先要設(shè)計(jì)一個(gè)EEPROM的Verilog HDL模型,而設(shè)計(jì)這樣一個(gè)模型我們需要仔細(xì)地閱讀和分析EEPROM器件的說明書,因?yàn)镋EPROM不是我們要設(shè)計(jì)的對(duì)象,而是我們驗(yàn)證設(shè)計(jì)對(duì)象所需要的器件,所以只需設(shè)計(jì)一個(gè)EEPROM的行為模型,而不需要可綜合風(fēng)格的模型,這就大大簡(jiǎn)化了設(shè)計(jì)過程。
下面的Verilog HDL程序就是這個(gè)EEPROM(AT24C02/4/8/16) 能完成一個(gè)字節(jié)數(shù)據(jù)讀寫的部分行為模型,請(qǐng)讀者查閱AT24C02/4/8/16說明書,對(duì)照下面的Verilog HDL程序理解設(shè)計(jì)的要點(diǎn)。因?yàn)檫@一程序是我們自己編寫的有不完善之處敬請(qǐng)指正。
這里只對(duì)在操作中用到的信號(hào)線進(jìn)行模擬,對(duì)于沒有用到的信號(hào)線就略去了。對(duì)EEPROM用于基本總線操作的引腳SCL和SDA說明如下:SCL,串行時(shí)鐘端,這個(gè)信號(hào)用于對(duì)輸入和輸出數(shù)據(jù)的同步,寫入串行EEPROM的數(shù)據(jù)用其上升沿同步,輸出數(shù)據(jù)用其下降沿同步;SDA,串行數(shù)據(jù)(/地址)輸入/輸出端。
`timescale 1ns/1ns
`define timeslice 100
module EEPROM(scl, sda);
input scl; //串行時(shí)鐘線
inout sda; //串行數(shù)據(jù)線
reg out_flag; //SDA數(shù)據(jù)輸出的控制信號(hào)
reg[7:0] memory[2047:0];
reg[10:0] address;
reg[7:0] memory_buf;
reg[7:0] sda_buf; //SDA 數(shù)據(jù)輸出寄存器
reg[7:0] shift; //SDA 數(shù)據(jù)輸入寄存器
reg[7:0] addr_byte; //EEPROM 存儲(chǔ)單元地址寄存器
reg[7:0] ctrl_byte; //控制字寄存器
reg[1:0] State; //狀態(tài)寄存器
integer i;
//--------------------------------------------------------------
parameter r7= 8’b10101111,w7= 8‘b10101110, //main7
r6= 8’b10101101,w6= 8‘b10101100, //main6
r5= 8’b10101011,w5= 8‘b10101010, //main5
r4= 8’b10101001,w4= 8‘b10101000, //main4
r3= 8’b10100111,w3= 8‘b10100110, //main3
r2= 8’b10100101,w2= 8‘b10100100, //main2
r1= 8’b10100011,w1= 8‘b10100010, //main1
r0= 8’b10100001,w0= 8‘b10100000; //main0
//--------------------------------------------------------------
assign sda = (out_flag == 1) ? sda_buf[7] : 1’bz;
//―――――――寄存器和存儲(chǔ)器初始化―――――――――――――――
initial
begin
addr_byte = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
State = 2‘b00;
memory_buf = 0;
address = 0;
shift = 0;
for(i=0;i《=2047;i=i+1)
memory[i]=0;
end
//------------ 啟動(dòng)信號(hào) -----------------------------
always @ (negedge sda)
if(scl == 1 )
begin
State = State + 1;
if(State == 2’b11)
disable write_to_eeprm;
end
//------------ 主狀態(tài)機(jī) --------------------------
always @(posedge sda)
if (scl == 1 ) //停止操作
stop_W_R;
else
begin
casex(State)
2‘b01:
begin
read_in;
if(ctrl_byte==w7||ctrl_byte==w6||ctrl_byte==w5
||ctrl_byte==w4||ctrl_byte==w3||ctrl_byte==w2
||ctrl_byte==w1||ctrl_byte==w0)
begin
State = 2’b10;
write_to_eeprm; //寫操作
end
else
State = 2‘b00;
end
2’b11:
read_from_eeprm; //讀操作
default:
State=2‘b00;
endcase
end
//------------- 操作停止------------------------------
task stop_W_R;
begin
State =2’b00; //狀態(tài)返回為初始狀態(tài)
addr_byte = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
end
endtask
//------------- 讀進(jìn)控制字和存儲(chǔ)單元地址 ------------------------
task read_in;
begin
shift_in(ctrl_byte);
shift_in(addr_byte);
end
endtask
//------------EEPROM 的寫操作---------------------------------------
task write_to_eeprm;
begin
shift_in(memory_buf);
address = {ctrl_byte[3:1],addr_byte};
memory[address] = memory_buf;
$display(“eeprm----memory[%0h]=%0h”,address,memory[address]);
State =2‘b00; //回到0狀態(tài)
end
endtask
//-----------EEPROM 的讀操作----------------------------------------
task read_from_eeprm;
begin
shift_in(ctrl_byte);
if(ctrl_byte==r7||ctrl_byte==r6||ctrl_byte==r5||ctrl_byte==r4
||ctrl_byte==r3||ctrl_byte==r2||ctrl_byte==r1||ctrl_byte==r0)
begin
address = {ctrl_byte[3:1],addr_byte};
sda_buf = memory[address];
shift_out;
State= 2’b00;
end
end
endtask
//-----SDA 數(shù)據(jù)線上的數(shù)據(jù)存入寄存器,數(shù)據(jù)在SCL的高電平有效-------------
task shift_in;
output [7:0] shift;
begin
@ (posedge scl) shift[7] = sda;
@ (posedge scl) shift[6] = sda;
@ (posedge scl) shift[5] = sda;
@ (posedge scl) shift[4] = sda;
@ (posedge scl) shift[3] = sda;
@ (posedge scl) shift[2] = sda;
@ (posedge scl) shift[1] = sda;
@ (posedge scl) shift[0] = sda;
@ (negedge scl)
begin
#`timeslice ;
out_flag = 1; //應(yīng)答信號(hào)輸出
sda_buf = 0;
end
@(negedge scl)
#`timeslice out_flag = 0;
end
endtask
//―――EEPROM 存儲(chǔ)器中的數(shù)據(jù)通過SDA 數(shù)據(jù)線輸出,數(shù)據(jù)在SCL 低電平時(shí)變化
task shift_out;
begin
out_flag = 1;
for(i=6;i》=0;i=i-1)
begin
@ (negedge scl);
#`timeslice;
sda_buf = sda_buf《《1;
end
@(negedge scl) #`timeslice sda_buf[7] = 1; //非應(yīng)答信號(hào)輸出
@(negedge scl) #`timeslice out_flag = 0;
end
endtask
endmodule
###2 ) EEPROM讀寫器的可綜合的Verilog HDL模型
下面的程序是一個(gè)串行EEPROM讀寫器的可綜合的Verilog HDL模型,它接收來自信號(hào)源模型產(chǎn)生的讀信號(hào)、寫信號(hào)、并行地址信號(hào)、并行數(shù)據(jù)信號(hào),并把它們轉(zhuǎn)換為相應(yīng)的串行信號(hào)發(fā)送到串行EEPROM(AT24C02/4/8/16) 的行為模型中去;它還發(fā)送應(yīng)答信號(hào) (ACK)到信號(hào)源模型,以便讓信號(hào)源來調(diào)節(jié)發(fā)送或接收數(shù)據(jù)的速度以配合EEPROM模型的接收(寫)和發(fā)送(讀)數(shù)據(jù)。因?yàn)樗俏覀兊脑O(shè)計(jì)對(duì)象,所以它不但要仿真正確無誤,還需要可綜合。
這個(gè)程序基本上由兩部分組成:開關(guān)組合電路和控制時(shí)序電路,見圖5。開關(guān)電路在控制時(shí)序電路的控制下按照設(shè)計(jì)的要求有節(jié)奏的打開或閉合,這樣SDA可以按I2C 數(shù)據(jù)總線的格式輸出或輸入,SDA和SCL一起完成EEPROM的讀寫操作。
電路最終用同步有限狀態(tài)機(jī)(FSM)的設(shè)計(jì)方法實(shí)現(xiàn)。程序?qū)崉t上是一個(gè)嵌套的狀態(tài)機(jī),由主狀態(tài)機(jī)和從狀態(tài)機(jī)通過由控制線啟動(dòng)的總線在不同的輸入信號(hào)的情況下構(gòu)成不同功能的較復(fù)雜的有限狀態(tài)機(jī),這個(gè)有限狀態(tài)機(jī)只有唯一的驅(qū)動(dòng)時(shí)鐘CLK。
根據(jù)串行EEPROM的讀寫操作時(shí)序可知,用5個(gè)狀態(tài)時(shí)鐘可以完成寫操作,用7個(gè)狀態(tài)時(shí)鐘可以完成讀操作,由于讀寫操作的狀態(tài)中有幾個(gè)狀態(tài)是一致的,用一個(gè)嵌套的狀態(tài)機(jī)即可。狀態(tài)轉(zhuǎn)移如圖6,程序由一個(gè)讀寫大任務(wù)和若干個(gè)較小的任務(wù)所組成,其狀態(tài)機(jī)采用獨(dú)熱編碼,若需改變狀態(tài)編碼,只需改變程序中的parameter定義即可。讀者可以通過模仿這一程序來編寫較復(fù)雜的可綜合Verilog HDL模塊程序。這個(gè)設(shè)計(jì)已通過后仿真,并可在FPGA上實(shí)現(xiàn)布局布線。
`timescale 1ns/1ns
module EEPROM_WR(SDA,SCL,ACK,RESET,CLK,WR,RD,ADDR,DATA);
output SCL; //串行時(shí)鐘線
output ACK; //讀寫一個(gè)周期的應(yīng)答信號(hào)
input RESET; //復(fù)位信號(hào)
input CLK; //時(shí)鐘信號(hào)輸入
input WR,RD; //讀寫信號(hào)
input[10:0] ADDR; //地址線
inout SDA; //串行數(shù)據(jù)線
inout[7:0] DATA; //并行數(shù)據(jù)線
reg ACK;
reg SCL;
reg WF,RF; //讀寫操作標(biāo)志
reg FF; //標(biāo)志寄存器
reg [1:0] head_buf; //啟動(dòng)信號(hào)寄存器
reg[1:0] stop_buf; //停止信號(hào)寄存器
reg [7:0] sh8out_buf; //EEPROM寫寄存器
reg [8:0] sh8out_state; //EEPROM 寫狀態(tài)寄存器
reg [9:0] sh8in_state; //EEPROM 讀狀態(tài)寄存器
reg [2:0] head_state; //啟動(dòng)狀態(tài)寄存器
reg [2:0] stop_state; //停止?fàn)顟B(tài)寄存器
reg [10:0] main_state; //主狀態(tài)寄存器
reg [7:0] data_from_rm; //EEPROM讀寄存器
reg link_sda; //SDA 數(shù)據(jù)輸入EEPROM開關(guān)
reg link_read; //EEPROM讀操作開關(guān)
reg link_head; //啟動(dòng)信號(hào)開關(guān)
reg link_write; //EEPROM寫操作開關(guān)
reg link_stop; //停止信號(hào)開關(guān)
wire sda1,sda2,sda3,sda4;
//--------------串行數(shù)據(jù)在開關(guān)的控制下有次序的輸出或輸入-------------------
assign sda1 = (link_head) ? head_buf[1] : 1‘b0;
assign sda2 = (link_write) ? sh8out_buf[7] : 1’b0;
assign sda3 = (link_stop) ? stop_buf[1] : 1‘b0;
assign sda4 = (sda1 | sda2 | sda3);
assign SDA = (link_sda) ? sda4 : 1’bz;
assign DATA = (link_read) ? data_from_rm : 8‘hzz;
//--------------------------------主狀態(tài)機(jī)狀態(tài)------------------------------------------
parameter
Idle = 11’b00000000001,
Ready = 11‘b00000000010,
Write_start = 11’b00000000100,
Ctrl_write = 11‘b00000001000,
Addr_write = 11’b00000010000,
Data_write = 11‘b00000100000,
Read_start = 11’b00001000000,
Ctrl_read = 11‘b00010000000,
Data_read = 11’b00100000000,
Stop = 11‘b01000000000,
Ackn = 11’b10000000000,
//-------------------------并行數(shù)據(jù)串行輸出狀態(tài)-----------------------------
sh8out_bit7 = 9‘b000000001,
sh8out_bit6 = 9’b000000010,
sh8out_bit5 = 9‘b000000100,
sh8out_bit4 = 9’b000001000,
sh8out_bit3 = 9‘b000010000,
sh8out_bit2 = 9’b000100000,
sh8out_bit1 = 9‘b001000000,
sh8out_bit0 = 9’b010000000,
sh8out_end = 9‘b100000000;
//--------------------------串行數(shù)據(jù)并行輸出狀態(tài)----------------------------
parameter sh8in_begin = 10’b0000000001,
sh8in_bit7 = 10‘b0000000010,
sh8in_bit6 = 10’b0000000100,
sh8in_bit5 = 10‘b0000001000,
sh8in_bit4 = 10’b0000010000,
sh8in_bit3 = 10‘b0000100000,
sh8in_bit2 = 10’b0001000000,
sh8in_bit1 = 10‘b0010000000,
sh8in_bit0 = 10’b0100000000,
sh8in_end = 10‘b1000000000,
//---------------------------------啟動(dòng)狀態(tài)----------------------------------
head_begin = 3’b001,
head_bit = 3‘b010,
head_end = 3’b100,
//---------------------------------停止?fàn)顟B(tài)----------------------------------
stop_begin = 3‘b001,
stop_bit = 3’b010,
stop_end = 3‘b100;
parameter YES = 1,
NO = 0;
//-------------產(chǎn)生串行時(shí)鐘,為輸入時(shí)鐘的二分頻-------------------
always @(negedge CLK)
if(RESET)
SCL 《= 0;
else
SCL 《= ~SCL;
//-----------------------------主狀態(tài)程序----------------------------------
always @ (posedge CLK)
if(RESET)
begin
link_read 《= NO;
link_write 《= NO;
link_head 《= NO;
link_stop 《= NO;
link_sda 《= NO;
ACK 《= 0;
RF 《= 0;
WF 《= 0;
FF 《= 0;
main_state 《= Idle;
end
else
begin
casex(main_state)
Idle:
begin
link_read 《= NO;
link_write 《= NO;
link_head 《= NO;
link_stop 《= NO;
link_sda 《= NO;
if(WR)
begin
WF 《= 1;
main_state 《= Ready ;
end
else if(RD)
begin
RF 《= 1;
main_state 《= Ready ;
end
else
begin
WF 《= 0;
RF 《= 0;
main_state 《= Idle;
end
end
Ready:
begin
link_read 《= NO;
link_write 《= NO;
link_stop 《= NO;
link_head 《= YES;
link_sda 《= YES;
head_buf[1:0] 《= 2’b10;
stop_buf[1:0] 《= 2‘b01;
head_state 《= head_begin;
FF 《= 0;
ACK 《= 0;
main_state 《= Write_start;
end
Write_start:
if(FF == 0)
shift_head;
else
begin
sh8out_buf[7:0] 《= {1’b1,1‘b0,1’b1,1‘b0,ADDR[10:8],1’b0};
link_head 《= NO;
link_write 《= YES;
FF 《= 0;
sh8out_state 《= sh8out_bit6;
main_state 《= Ctrl_write;
end
Ctrl_write:
if(FF ==0)
shift8_out;
else
begin
sh8out_state 《= sh8out_bit7;
sh8out_buf[7:0] 《= ADDR[7:0];
FF 《= 0;
main_state 《= Addr_write;
end
Addr_write:
if(FF == 0)
shift8_out;
else
begin
FF 《= 0;
if(WF)
begin
sh8out_state 《= sh8out_bit7;
sh8out_buf[7:0] 《= DATA;
main_state 《= Data_write;
end
if(RF)
begin
head_buf 《= 2‘b10;
head_state 《= head_begin;
main_state 《= Read_start;
end
end
Data_write:
if(FF == 0)
shift8_out;
else
begin
stop_state 《= stop_begin;
main_state 《= Stop;
link_write 《= NO;
FF 《= 0;
end
Read_start:
if(FF == 0)
shift_head;
else
begin
sh8out_buf 《= {1’b1,1‘b0,1’b1,1‘b0,ADDR[10:8],1’b1};
link_head 《= NO;
link_sda 《= YES;
link_write 《= YES;
FF 《= 0;
sh8out_state 《= sh8out_bit6;
main_state 《= Ctrl_read;
end
Ctrl_read:
if(FF == 0)
shift8_out;
else
begin
link_sda 《= NO;
link_write 《= NO;
FF 《= 0;
sh8in_state 《= sh8in_begin;
main_state 《= Data_read;
end
Data_read:
if(FF == 0)
shift8in;
else
begin
link_stop 《= YES;
link_sda 《= YES;
stop_state 《= stop_bit;
FF 《= 0;
main_state 《= Stop;
end
Stop:
if(FF == 0)
shift_stop;
else
begin
ACK 《= 1;
FF 《= 0;
main_state 《= Ackn;
end
Ackn:
begin
ACK 《= 0;
WF 《= 0;
RF 《= 0;
main_state 《= Idle;
end
default: main_state 《= Idle;
endcase
end
//------------------------串行數(shù)據(jù)轉(zhuǎn)換為并行數(shù)據(jù)任務(wù)----------------------------------
task shift8in;
begin
casex(sh8in_state)
sh8in_begin:
sh8in_state 《= sh8in_bit7;
sh8in_bit7: if(SCL)
begin
data_from_rm[7] 《= SDA;
sh8in_state 《= sh8in_bit6;
end
else
sh8in_state 《= sh8in_bit7;
sh8in_bit6: if(SCL)
begin
data_from_rm[6] 《= SDA;
sh8in_state 《= sh8in_bit5;
end
else
sh8in_state 《= sh8in_bit6;
sh8in_bit5: if(SCL)
begin
data_from_rm[5] 《= SDA;
sh8in_state 《= sh8in_bit4;
end
else
sh8in_state 《= sh8in_bit5;
sh8in_bit4: if(SCL)
begin
data_from_rm[4] 《= SDA;
sh8in_state 《= sh8in_bit3;
end
else
sh8in_state 《= sh8in_bit4;
sh8in_bit3: if(SCL)
begin
data_from_rm[3] 《= SDA;
sh8in_state 《= sh8in_bit2;
end
else
sh8in_state 《= sh8in_bit3;
sh8in_bit2: if(SCL)
begin
data_from_rm[2] 《= SDA;
sh8in_state 《= sh8in_bit1;
end
else
sh8in_state 《= sh8in_bit2;
sh8in_bit1: if(SCL)
begin
data_from_rm[1] 《= SDA;
sh8in_state 《= sh8in_bit0;
end
else
sh8in_state 《= sh8in_bit1;
sh8in_bit0: if(SCL)
begin
data_from_rm[0] 《= SDA;
sh8in_state 《= sh8in_end;
end
else
sh8in_state 《= sh8in_bit0;
sh8in_end: if(SCL)
begin
link_read 《= YES;
FF 《= 1;
sh8in_state 《= sh8in_bit7;
end
else
sh8in_state 《= sh8in_end;
default: begin
link_read 《= NO;
sh8in_state 《= sh8in_bit7;
end
endcase
end
endtask
//------------------------------ 并行數(shù)據(jù)轉(zhuǎn)換為串行數(shù)據(jù)任務(wù) ---------------------------
task shift8_out;
begin
casex(sh8out_state)
sh8out_bit7:
if(!SCL)
begin
link_sda 《= YES;
link_write 《= YES;
sh8out_state 《= sh8out_bit6;
end
else
sh8out_state 《= sh8out_bit7;
sh8out_bit6:
if(!SCL)
begin
link_sda 《= YES;
link_write 《= YES;
sh8out_state 《= sh8out_bit5;
sh8out_buf 《= sh8out_buf《《1;
end
else
sh8out_state 《= sh8out_bit6;
sh8out_bit5:
if(!SCL)
begin
sh8out_state 《= sh8out_bit4;
sh8out_buf 《= sh8out_buf《《1;
end
else
sh8out_state 《= sh8out_bit5;
sh8out_bit4:
if(!SCL)
begin
sh8out_state 《= sh8out_bit3;
sh8out_buf 《= sh8out_buf《《1;
end
else
sh8out_state 《= sh8out_bit4;
sh8out_bit3:
if(!SCL)
begin
sh8out_state 《= sh8out_bit2;
sh8out_buf 《= sh8out_buf《《1;
end
else
sh8out_state 《= sh8out_bit3;
sh8out_bit2:
if(!SCL)
begin
sh8out_state 《= sh8out_bit1;
sh8out_buf 《= sh8out_buf《《1;
end
else
sh8out_state 《= sh8out_bit2;
sh8out_bit1:
if(!SCL)
begin
sh8out_state 《= sh8out_bit0;
sh8out_buf 《= sh8out_buf《《1;
end
else
sh8out_state 《= sh8out_bit1;
sh8out_bit0:
if(!SCL)
begin
sh8out_state 《= sh8out_end;
sh8out_buf 《= sh8out_buf《《1;
end
else
sh8out_state 《= sh8out_bit0;
sh8out_end:
if(!SCL)
begin
link_sda 《= NO;
link_write 《= NO;
FF 《= 1;
end
else
sh8out_state 《= sh8out_end;
endcase
end
endtask
//--------------------------- 輸出啟動(dòng)信號(hào)任務(wù) ---------------------------------
task shift_head;
begin
casex(head_state)
head_begin:
if(!SCL)
begin
link_write 《= NO;
link_sda 《= YES;
link_head 《= YES;
head_state 《= head_bit;
end
else
head_state 《= head_begin;
head_bit:
if(SCL)
begin
FF 《= 1;
head_buf 《= head_buf《《1;
head_state 《= head_end;
end
else
head_state 《= head_bit;
head_end:
if(!SCL)
begin
link_head 《= NO;
link_write 《= YES;
end
else
head_state 《= head_end;
endcase
end
endtask
//--------------------------- 輸出停止信號(hào)任務(wù) --------------------------------------
task shift_stop;
begin
casex(stop_state)
stop_begin: if(!SCL)
begin
link_sda 《= YES;
link_write 《= NO;
link_stop 《= YES;
stop_state 《= stop_bit;
end
else
stop_state 《= stop_begin;
stop_bit: if(SCL)
begin
stop_buf 《= stop_buf《《1;
stop_state 《= stop_end;
end
else
stop_state《= stop_bit;
stop_end: if(!SCL)
begin
link_head 《= NO;
link_stop 《= NO;
link_sda 《= NO;
FF 《= 1;
end
else
stop_state 《= stop_end;
endcase
end
endtask
endmodule
程序最終通過Synplify器的綜合,并在Actel 3200DX 系列的FPGA上實(shí)現(xiàn)布局布線,通過布線后仿真 。
3 ) EEPROM的信號(hào)源模塊和頂層模塊完成串行EEPROM讀寫器件的設(shè)計(jì)后,我們還需要做的重要一步是EEPROM讀寫器件的仿真。仿真可以分為前仿真和后仿真,前仿真是Verilog HDL的功能仿真,后仿真是Verilog HDL 代碼經(jīng)過綜合、布局布線后的時(shí)序仿真。
為此,我們還要編寫了用于EEPROM讀寫器件的仿真測(cè)試的信號(hào)源程序。這個(gè)信號(hào)源能產(chǎn)生相應(yīng)的讀信號(hào)、寫信號(hào)、并行地址信號(hào)、并行數(shù)據(jù)信號(hào),并能接收串行EEPROM讀寫器件的應(yīng)答信號(hào) (ACK),來調(diào)節(jié)發(fā)送或接收數(shù)據(jù)的速度。
在這個(gè)程序中,我們?yōu)榱吮WC串行EEPROM讀寫器件的正確性,可以進(jìn)行完整的測(cè)試,寫操作時(shí)輸入的地址信號(hào)和數(shù)據(jù)信號(hào)的數(shù)據(jù)通過系統(tǒng)命令readmemh和$fopen等系統(tǒng)命令讀者可以參考Verilog HDL的語法部分。最后我們把信號(hào)源、EEPROM和EEPROM讀寫器用頂層模塊連接在一起。在下面的程序就是這個(gè)信號(hào)源的Verilog HDL模型和頂層模塊。
信號(hào)源模型:
`timescale 1ns/1ns
`define timeslice 200
module Signal(RESET,CLK,RD,WR,ADDR,ACK,DATA);
output RESET; //復(fù)位信號(hào)
output CLK; //時(shí)鐘信號(hào)
output RD,WR; //讀寫信號(hào)
output[10:0] ADDR; //11位地址信號(hào)
input ACK; //讀寫周期的應(yīng)答信號(hào)
inout[7:0] DATA; //數(shù)據(jù)線
reg RESET;
reg CLK;
reg RD,WR;
reg W_R; //低位:寫操作;高位:讀操作
reg[10:0] ADDR;
reg[7:0] data_to_eeprom;
reg[10:0] addr_mem[0:255];
reg[7:0] data_mem[0:255];
reg[7:0] ROM[1:2048];
integer i,j;
integer OUTFILE;
assign DATA = (W_R) ? 8‘bz : data_to_eeprom ;
//------------------------------------時(shí)鐘信號(hào)輸入------------------------------
always #(`timeslice/2)
CLK = ~CLK;
//----------------------------------- 讀寫信號(hào)輸入------------------------------
initial
begin
RESET = 1;
i = 0;
j =0;
W_R = 0;
CLK = 0;
RD = 0;
WR = 0;
#1000 ;
RESET = 0;
repeat(15) //連續(xù)寫15次數(shù)據(jù)
begin
#(5*`timeslice);
WR = 1;
#(`timeslice);
WR = 0;
@ (posedge ACK);
end
#(10*`timeslice);
W_R = 1; //開始讀操作
repeat(15) //連續(xù)讀15次數(shù)據(jù)
begin
#(5*`timeslice);
RD = 1;
#(`timeslice);
RD = 0;
@ (posedge ACK);
end
end
//-----------------------------------------寫操作-----------------------------
initial
begin
$display(“writing-----writing-----writing-----writing”);
# (2*`timeslice);
for(i=0;i《=15;i=i+1)
begin
ADDR = addr_mem[i];
data_to_eeprom = data_mem[i];
$fdisplay(OUTFILE,“@%0h %0h”,ADDR, data_to_eeprom);
@(posedge ACK) ;
end
end
//----------------------------------------讀操作----------------------------
initial
@(posedge W_R)
begin
ADDR = addr_mem[0];
$fclose(OUTFILE);
$readmemh(“。/eeprom.dat”,ROM);
$display(“Begin READING-----READING-----READING-----READING”);
for(j = 0; j 《= 15; j = j+1)
begin
ADDR = addr_mem[j];
@(posedge ACK);
if(DATA == ROM[ADDR])
$display(“DATA %0h == ROM[%0h]---READ RIGHT”,DATA,ADDR);
else
$display(“DATA %0h != ROM[%0h]---READ WRONG”,DATA,ADDR);
end
end
initial
begin
OUTFILE = $fopen(“。/eeprom.dat”);
$readmemh(“。/addr.dat”,addr_mem); //地址數(shù)據(jù)存入地址存儲(chǔ)器
$readmemh(“。/data.dat”,data_mem); //寫入EEPROM的數(shù)據(jù)存入數(shù)據(jù)存儲(chǔ)器
end
endmodule
頂層模塊:
`include “。/Signal.v”
`include “。/EEPROM.v”
`include “。/EEPROM_WR.v”
`timescale 1ns/1ns
module Top;
wire RESET;
wire CLK;
wire RD,WR;
wire ACK;
wire[10:0] ADDR;
wire[7:0] DATA;
wire SCL;
wire SDA;
Signal signal(.RESET(RESET),.CLK(CLK),.RD(RD),
.WR(WR),.ADDR(ADDR),.ACK(ACK),.DATA(DATA));
EEPROM_WR eeprom_wr(.RESET(RESET),.SDA(SDA),.SCL(SCL),.ACK(ACK),
.CLK(CLK),.WR(WR),.RD(RD),.ADDR(ADDR),.DATA(DATA));
EEPROM eeprom(.sda(SDA),.scl(SCL));
endmodule
通過前后仿真可以驗(yàn)證程序的正確性。這里給出的是EEPROM讀寫時(shí)序的前仿真波形。后仿真波形除SCL和SDA與CLK有些延遲外,信號(hào)的邏輯關(guān)系與前仿真一致:
說明:
以上編程、仿真、綜合在PC WINDOWS NT 4.0操作系統(tǒng)、Synplify 、Actel Designer 、Altera Maxplus9.3及ModelSim Verilog環(huán)境下通過前后仿真,也在Unix Cadence Verilog-XL上通過前、后仿真(可綜合到各種FPGA和ASIC工藝)。
編輯:jq
-
Verilog
+關(guān)注
關(guān)注
28文章
1343瀏覽量
109931 -
HDL
+關(guān)注
關(guān)注
8文章
327瀏覽量
47309 -
讀寫器
+關(guān)注
關(guān)注
3文章
650瀏覽量
38800
原文標(biāo)題:Verilog復(fù)雜時(shí)序邏輯電路設(shè)計(jì)實(shí)踐
文章出處:【微信號(hào):gh_339470469b7d,微信公眾號(hào):FPGA與數(shù)據(jù)通信】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論