<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > 對C語(yǔ)言編程者的Verilog開(kāi)發(fā)指南實(shí)例

對C語(yǔ)言編程者的Verilog開(kāi)發(fā)指南實(shí)例

作者: 時(shí)間:2017-06-06 來(lái)源:網(wǎng)絡(luò ) 收藏
本文舉例說(shuō)明了如何用軟件實(shí)現脈寬調制(PWM),如何將該設計轉換成一個(gè)可以在FPGA中運行的邏輯塊,并能利用存儲器映射I/O接口通過(guò)軟件完成對該邏輯塊的控制。通過(guò)理解本文討論的概念和內容,沒(méi)有太多硬件知識的軟件開(kāi)發(fā)人員也能掌握在FPGA上開(kāi)發(fā)硬件的技能。

在不遠的將來(lái),嵌入式系統設計師將能夠根據哪個(gè)更有利于解決設計問(wèn)題來(lái)自由選擇硬件和軟件方案。但直到現在,對于那些想學(xué)習硬件設計的軟件工程師來(lái)說(shuō)不少障礙仍然很難逾越。由于硬件描述語(yǔ)言和編程語(yǔ)言非常相似,因此最終這些障礙會(huì )消失。另外,市場(chǎng)上已有好幾種低成本的演示板,上面包含現場(chǎng)可編程門(mén)陣列(FPGA)、微處理器以及相應工具,軟件開(kāi)發(fā)人員可以借此來(lái)學(xué)習硬件設計。

本文舉例說(shuō)明了一個(gè)使用FPGA的新設計流程,我們從中可以知道如何用軟件實(shí)現PWM,然后如何將該設計轉換成一個(gè)可以在FPGA中運行的邏輯塊,并能利用存儲器映射I/O接口通過(guò)軟件完成對該邏輯塊的控制。

軟硬件劃分

現在的情況與以前有所不同,軟件工程師能夠方便地參與到硬件設計中。不管是硬件模塊還是軟件模塊現在都可以用編程語(yǔ)言進(jìn)行設計。眾所周知,C語(yǔ)言是嵌入式軟件設計的通用語(yǔ)言。在硬件設計方面,Verilog則是流行的選擇(用VHDL的人也很多)。Verilog的語(yǔ)法和結構與C編程語(yǔ)言非常相似,從本文的例子中也可以看到這一點(diǎn)。

同時(shí),硬件的升級和修改也變得越來(lái)越方便。以前可以通過(guò)下載新的可執行映像文件升級軟件,但對硬件卻行不通?,F在情況不同了。就像軟件開(kāi)發(fā)人員能夠快速編輯、重新編譯、然后將新代碼下載到存儲器那樣,使用可編程邏輯器件的硬件設計者也能做類(lèi)似的事情??删幊踢壿嫺淖兞饲度胧较到y的設計方法,設計者可以像修改軟件那樣方便地修改硬件。換句話(huà)說(shuō),在設計和調試階段,設計者能夠靈活選擇軟件方式或硬件方式來(lái)作為完成任務(wù)的最佳方式。

設計者無(wú)需太多的硬件知識就可以利用FPGA供應商提供的工具輕松地開(kāi)發(fā)出可編程邏輯嵌入式系統。例如,Altera公司的SOPC Builder能幫助系統設計師從已有的庫中選擇和配置外圍電路,并增加用來(lái)創(chuàng )建和連接外圍電路的用戶(hù)邏輯。加上一些可編程邏輯和硬件知識,軟件工程師就能夠充分利用硬件的優(yōu)勢改進(jìn)他們的系統。

PWM軟件

PWM控制器會(huì )產(chǎn)生一連串脈沖。通常需要規定脈沖的周期和寬度。占空比被定義為脈沖寬度與周期的比值。PWM有著(zhù)廣泛的應用,大多數情況下用于控制模擬電路。因為數字信號連續變化的速率相對較快(當然取決于信號周期),因此最終會(huì )形成一個(gè)用來(lái)控制模擬設備的平均電壓值。當PWM脈沖流應用于馬達時(shí),馬達的轉速就能正比于占空比(從0%到100%)。如果占空比增加,馬達轉速就會(huì )提高,反之,如果占空比減小,馬達的轉速隨之也會(huì )降低。

用軟件編寫(xiě)這樣一個(gè)PWM控制器是相對比較容易的任務(wù),但它有助于我們簡(jiǎn)明扼要地描述如何用Verilog設計硬件。清單1給出了PWM的C代碼。

清單1:完全用軟件實(shí)現的位脈沖PWM控制器。

void
pwmTask(uint32_t pulse_width, uint32_t period)
{
uint32_t time_on=pulse_width;
uint32_t time_off=period-pulse_width;
while (1)
{
pwm_output=1;
sleep(time_on);
pwm_output=0;
sleep(time_off);
}
}

根據脈寬(pulse_width)和周期(period)參數值,計算出輸出為高電平和低電平的時(shí)間。接下來(lái)將輸出引腳置為高電平,并等待time_on設定的時(shí)間值之后,將輸出變?yōu)榈碗娖?,并等待time_off參數設定的時(shí)間值。下個(gè)周期再重復這樣的過(guò)程,并無(wú)限循環(huán)下去。

Verilog模塊

清單2給出了一個(gè)簡(jiǎn)單的Verilog模塊,實(shí)現帶異步復位功能的8位寬寄存器。寄存器的輸入“in”在時(shí)鐘的上升沿被賦值到輸出“out”,直到clr_n復位信號的下降沿到來(lái)(此時(shí)輸出將被賦值為0)。

清單2:實(shí)現帶異步復位功能8位寬寄存器的Verilog編寫(xiě)模塊。
module simple_register(in, out, clr_n, clk, a);

//端口聲明
input
input
input [7:0]
input
output [7:0]
clr_n;
clk;
in;
a;
out;

//信號聲明
reg [7:0]
wire
out;
a;

//實(shí)現帶異步清除的寄存器
always @(posedge clk or negedge clr_n)
begin
if (clr_n==0) // could also be written if (!clr_n)
out=0;
else
out=in;
end

//連續賦值
assign a=!out[0];
endmodule

粗略地看Verilog與C語(yǔ)言有許多相似之處。分號用于結束每個(gè)語(yǔ)句,注釋符也是相同的(/* ... */和// 都是熟悉的),運算符“==”也用來(lái)測試相等性。Verilog的if..then..else語(yǔ)法與C語(yǔ)言的也非常相似,只是Verilog用關(guān)鍵字begin和end代替了C的大括號。事實(shí)上,關(guān)鍵字begin和end對于單語(yǔ)句塊來(lái)說(shuō)是可有可無(wú)的,就與C中的大括號用法一樣。Verilog和C都對大小寫(xiě)敏感。

當然,硬件和軟件的一個(gè)重要區別是它們的“運行”方式。硬件設計中用到的許多單元都是并行工作的。一旦設備電源開(kāi)啟,硬件的每個(gè)單元就會(huì )一直處于運行狀態(tài)。雖然根據具體的控制邏輯和數據輸入,設備的一些單元可能不會(huì )改變它們的輸出信號,但它們還是一直在“運行”中。

相反,在同一時(shí)刻整個(gè)軟件設計中只有一小部分(即使是多軟件任務(wù)也只有一個(gè)任務(wù))在執行。如果只有一個(gè)處理器,同一時(shí)間點(diǎn)只能有一條指令在執行。軟件的其它部分可以被認為處于休眠狀態(tài),這與硬件有很大的不同。變量可能以一個(gè)有效值而存在,但大多數時(shí)間里它們都不在使用狀態(tài)。

軟硬件的不同行為會(huì )直接導致硬件和軟件代碼編程方式的不同。軟件是串行執行的,每一行代碼的執行都要等到前一行代碼執行完畢后才能進(jìn)行(中斷的非線(xiàn)性或操作系統的命令除外)。
//-------------------------------------------------------------------------------------------------------------
一個(gè)Verilog模塊的開(kāi)頭是關(guān)鍵字module,緊跟其后的是模塊名稱(chēng)和端口列表,端口列表列出了該模塊用到的所有輸入輸出名稱(chēng)。接下來(lái)是端口聲明部分。注意:所有的輸入輸出既出現在模塊第一行的端口列表中,也會(huì )出現在端口聲明(declaration)部分中。

在Verilog中有二種類(lèi)型的內部信號用得比較多,它們是reg和wire。它們具有不同的功能。所有端口都有一個(gè)名稱(chēng)相同且聲明為wire的信號。因此連線(xiàn)line被聲明為wire不是必要的。reg會(huì )保持上次的賦值,因此不需要每次都進(jìn)行驅動(dòng)。wire型信號用于異步邏輯,有時(shí)也用來(lái)連接信號。因為reg可以保持上次的值,因此輸入不能被聲明為reg類(lèi)型。在Verilog模塊中可以在任何時(shí)候異步地將輸入改變?yōu)槿魏问录?。reg和wire的主要區別是,reg類(lèi)型的信號只能在過(guò)程塊(后面會(huì )談到)中賦值,而wire類(lèi)型的信號只能在過(guò)程塊外賦值。這兩種信號類(lèi)型都可以出現在過(guò)程塊內部和外部的賦值運算符右邊。

使用關(guān)鍵字reg并不一定意味著(zhù)編譯器會(huì )創(chuàng )建一個(gè)寄存器,理解這一點(diǎn)是非常重要的。清單2的代碼中有一個(gè)reg類(lèi)型8位寬的內部信號out。該模塊使用寄存器源于always模塊(過(guò)程塊的一種)的編程方式。值得注意的是,信號a是一個(gè)wire類(lèi)型,因此只能在連續賦值(continuous assignment)語(yǔ)句中賦值,而reg類(lèi)型的out信號只能在always塊中賦值。

always塊是過(guò)程塊的一種,僅在某種變化發(fā)生時(shí)用于更新信號。always語(yǔ)句圓括號里的表達式組被稱(chēng)為敏感列表,格式是:(表達式or表達式…)

只要敏感列表中的任何一個(gè)表達式值為真,always塊中的代碼就會(huì )被執行。Verilog中用于上升沿和下降沿的關(guān)鍵字分別是posedge和negedge。這二個(gè)關(guān)鍵字經(jīng)常被用于敏感列表。在本例中,如果clk信號的上升沿或clr_n的下降沿信號發(fā)生時(shí),always塊內部的語(yǔ)句就會(huì )被執行。

為了用好寄存器,輸出必須在時(shí)鐘的上升沿得到更新(下降沿也可以,但上升沿更常見(jiàn)些)。增加negedge clr_n會(huì )使寄存器在clr_n信號的下降沿復位。但并不是所有的敏感列表都會(huì )包含關(guān)鍵字posedge或negedge,因此在實(shí)際硬件中并不總是存在真實(shí)的寄存器。

always塊內的第一條語(yǔ)句判斷clr_n信號的上升沿有沒(méi)有發(fā)生。如果有,下一行代碼把out置為0。這些代碼行實(shí)現了寄存器的異步復位功能。如果條件語(yǔ)句是:if(negedge clr_n and clk==1),那么該語(yǔ)句實(shí)現的就是基于時(shí)鐘的異步復位。

讀者可能已經(jīng)注意到,always塊中的賦值運算符與以關(guān)鍵字assign開(kāi)頭的連續賦值語(yǔ)句中用到的運算符不一樣。=運算符用于非阻塞性(nonblocking)賦值,而=運算符用于阻塞性(blocking)賦值。

在一組阻塞性賦值語(yǔ)句中,在下一個(gè)阻塞性賦值語(yǔ)句執行前需要計算并賦值第一個(gè)賦值語(yǔ)句。這一過(guò)程就象C語(yǔ)言中語(yǔ)句的順序執行。而非阻塞語(yǔ)句在執行時(shí),所有賦值語(yǔ)句的右邊被同時(shí)計算和賦值。連續賦值語(yǔ)句必須使用阻塞賦值語(yǔ)句(否則編譯器會(huì )報錯)。

為了減少代碼出錯的概率,建議在順序邏輯(例如希望以寄存器方式實(shí)現的邏輯)always塊中的所有賦值語(yǔ)句使用非阻塞性賦值語(yǔ)句。大多數always塊應該使用非阻塞性賦值語(yǔ)句。如果always塊都是組合邏輯,那么就需要使用阻塞性賦值語(yǔ)句。
//-------------------------------------------------------------------------------------------------------------
PWM硬件

編寫(xiě)存儲器映射硬件模塊的首要任務(wù)是以軟件方式?jīng)Q定寄存器映射圖。在PWM案例中,一般設計師希望能用軟件設置周期和脈寬。在硬件設計中用計數器統計系統時(shí)鐘周期數是非常容易的。因此要用到兩個(gè)寄存器,分別命名為pulse_width和period,并且都在時(shí)鐘周期內度量。表1給出了PWM的寄存器映射圖。

為了確定輸出信號,硬件可簡(jiǎn)單地通過(guò)將period和pulse_width寄存器內容作為運行中的計數器保持的輸出。

接下來(lái)要為PWM選擇端口,大多數端口可以依據總線(xiàn)架構而定。表2提供了通用存儲器映射PWM的信號描述概要。通常為低電平有效的信號命名做法是在信號名上加“_n”,對于控制信號更是如此。表2中的write_n和clr_n信號就是低電平有效的信號(下降沿觸發(fā))。

至此我們已經(jīng)定義好了硬件模塊的接口,接下來(lái)就可以開(kāi)始編寫(xiě)Verilog代碼了。清單3給出了一個(gè)實(shí)現例子。

清單3:用Verilog實(shí)現的PWM硬件。
module pwm (clk, write_data, cs, write_n, addr, clr_n, read_data, pwm_out);

input
input [31:0]
input
input
input
input
output [31:0]
output
clk;
write_data;
cs;
write_n;
addr;
clr_n;
read_data;
pwm_out;

reg [31:0]
reg [31:0]
reg [31:0]
reg
reg [31:0]
wire
period;
pulse_width;
counter;
off;
read_data;
period_en, pulse_width_en; //寫(xiě)使能

// 定義period和pulse_width寄存器的內容
always @(posedge clk or negedge clr_n)
begin
if (clr_n==0)
begin
period=32'h 00000000;
pulse_width=32'h 00000000;
end
else
begin
if (period_en)
period=write_data[31:0];
else
period=period;
if (pulse_width_en)
pulse_width=write_data[31:0];
else
pulse_width=pulse_width;
end
end

// period和pulse_width寄存器的讀訪(fǎng)問(wèn)
always @(addr or period or pulse_width)
if (addr == 0)
read_data=period;
else
read_data=pulse_width;
always @(posedge clk or negedge clr_n)
begin
if (clr_n==0)
counter=0;
else
if (counter>=period-1)
counter=0;
else
counter=counter+1;
end
always @(posedge clk or negedge clr_n)
begin
if (clr_n==0)
off=0;
else
if (counter>=pulse_width)
off = 1;
else
if (counter==0)
off=0;
else
off=off;
end

assign period_en = cs !write_n !addr;
assign pulse_width_en = cs !write_n addr;
//PWM輸出
assign pwm_out=!off;
endmodule

首先是端口說(shuō)明,接著(zhù)是內部信號說(shuō)明。構成PWM軟件控制接口的存儲器映射型寄存器被聲明為reg。該代碼行只允許以32位的方式訪(fǎng)問(wèn)這些存儲器映射型寄存器。如果需要8位或16位訪(fǎng)問(wèn),就必須將寄存器分割成4個(gè)8位寄存器,并增加字節使能信號邏輯。用Verilog代碼實(shí)現這一功能是非常簡(jiǎn)單的。always塊中已賦過(guò)值的所有信號都被聲明為reg類(lèi)型。聲明為wire類(lèi)型的信號是period和pulse_width寄存器寫(xiě)入使能信號。這些信號使用連續賦值語(yǔ)句進(jìn)行賦值。

清單的余下部分即是實(shí)際的代碼,共有4個(gè)always塊,最后還有幾個(gè)賦值語(yǔ)句。每個(gè)always塊描述一個(gè)信號或一組有相同基本行為(換句話(huà)說(shuō),使用相同的控制邏輯)的信號的行為。這是使代碼具有可讀性并能減少錯誤的Verilog代碼編寫(xiě)風(fēng)格。所有的always塊都有復位邏輯,當clr_n信號被證實(shí)(設為0)時(shí),復位邏輯將信號置為0。雖然這樣做并不是嚴格必需的,但這是一種良好的設計習慣,能使每個(gè)信號在復位時(shí)都有確定的值。

第一個(gè)always塊描述了寄存器映射中的寄存器行為。當正確的使能信號被證實(shí)時(shí),write_data寄存器值就被寫(xiě)入period或pulse_width寄存器中。這是改變任一寄存器值的唯一途徑。該文件底部的連續賦值語(yǔ)句定義了寫(xiě)入使能信號。當主寫(xiě)入使能信號和芯片選擇信號同時(shí)被證實(shí)時(shí),period和pulse_width寄存器的寫(xiě)入使能信號就被證實(shí),此時(shí)period和pulse_width的地址位應分別被置為0和1。

第二個(gè)always塊定義了寄存器映射圖中讀寄存器。Period寄存器位于外圍電路的基本地址處,pulse_width寄存器在后面32位字地址處。

第三和第四個(gè)always塊一起來(lái)決定PWM的輸出。第三個(gè)always塊實(shí)現計數器功能,它連續計數到period寄存器設置的值時(shí)復位到0,然后重新開(kāi)始計數。第四個(gè)always塊對該計數器值與pulse_width寄存器值進(jìn)行比較,當計數器值小于pulse_width值時(shí),PWM輸出保持高電平,否則設為低電平。

需要牢記的是不管在何種條件下每個(gè)信號都必須有明確的值?;仡櫼幌掠布幕拘袨樘卣?mdash;—“始終在運行”。例如在最后一個(gè)always塊(描述off信號的那個(gè)塊)中,代碼的最后行將off賦于它本身。最初看來(lái)好象比較奇怪,但如果沒(méi)有這一行的話(huà),off值將是不確定的。對這一情況保持跟蹤的最方便途徑是確保每次信號會(huì )在if語(yǔ)句中賦值,在相應的else語(yǔ)句中也賦值。

軟件訪(fǎng)問(wèn)

現在硬件完成了,可以利用寄存器映射圖中的寄存器通過(guò)軟件對PWM實(shí)施控制。讀者可以用一個(gè)簡(jiǎn)單的帶指針的數據結構連接PWM中的寄存器。

typedef volatile struct
{
uint32_t period;
uint32_t pulse_width;
} PWM;

例如,可以將PWM連接到LED。先初始化一個(gè)名為pLED、類(lèi)型為PWM*的變量,將其指向PWM基地址。這樣做實(shí)際上是將硬件抽象進(jìn)了一個(gè)數據結構。向pLED->period寫(xiě)入數據會(huì )設置或改變period值,向pLED->pulse_width寫(xiě)入數據將改變占空比,并導致LED的亮度增加或減少。如果使用的是閃爍型LED,只需把周期變長(cháng),讓肉眼清晰辨別開(kāi)和關(guān)的周期即可。

清單3所示的Verilog PWM實(shí)現在本例中是作為Altera的Nios處理器系統的外圍電路進(jìn)行測試的,可以利用前文所述的C結構通過(guò)軟件對它訪(fǎng)問(wèn)。Altera的SOPC Builder創(chuàng )建了宏,可以使ModelSim(明導資訊公司的一個(gè)硬件仿真器)中的協(xié)同仿真。在系統執行C代碼時(shí)可以利用ModelSim仿真器觀(guān)察到PWM信號以及其它系統信號的行為。

清單4給出了用于產(chǎn)生圖1所示PWM波形的C代碼。C代碼向PWM寄存器寫(xiě)入數據,創(chuàng )建出周期為5個(gè)時(shí)鐘周期、脈寬為4個(gè)時(shí)鐘周期的PWM輸出信號。請注意在波形的開(kāi)始處,由于period和pulse_width寄存器都被寫(xiě)入了數據,cs和wr_n信號被證實(shí)了二次(在寫(xiě)period寄存器時(shí)地址信號為低電平,在寫(xiě)pulse_width寄存器時(shí)地址信號變成了高電平)。

清單4:用于產(chǎn)生圖1所示PWM波形的測試軟件。
void
main(void)
{
PWM * const pLED=...
pLED->period=5;
pLED->pulse_width=4;
asm(nop);
asm(nop);
asm(nop);
pLED->pulse_width=2;
}

在寄存器中寫(xiě)入新值后,pwm_output信號開(kāi)始反映出變化。然后,只加入一些時(shí)延我們再看輸出,一些NOP指令被C代碼執行了。最終,脈寬變?yōu)?個(gè)周期,PWM波形相應也有了變化,但周期仍保持為5個(gè)時(shí)鐘周期。

設計嵌入式系統架構時(shí)最好將系統分成硬件和軟件二大模塊,以便充分利用各自的優(yōu)勢。隨著(zhù)開(kāi)發(fā)工具的不斷發(fā)展,軟件和硬件模塊的相互交換也變得越來(lái)越透明。

一旦充分理解了本文討論的概念和內容,也就掌握了在FPGA上開(kāi)發(fā)硬件的技能。FPGA能被用作微處理器系統中的一個(gè)存儲器映射式外圍電路,可以通過(guò)簡(jiǎn)單的編程實(shí)現接口。由于用硬件實(shí)現算法的速度快得多,將算法從軟件轉換成硬件可以極大地提高系統性能。這就是人們常說(shuō)的硬件加速,掌握這一技術(shù)是熟練使用可編程邏輯器件中被有效實(shí)現的可配置處理器的關(guān)鍵。從長(cháng)遠來(lái)看,即使是軟件工程師也能通過(guò)硬件加速提高系統性能和效率。


關(guān)鍵詞: CVerilog

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>