1. 程式人生 > >【FPGA黑金開發板】NIOSII那些事兒--基於AVALON匯流排的IP定製(十七)

【FPGA黑金開發板】NIOSII那些事兒--基於AVALON匯流排的IP定製(十七)

簡介

      NIOS II是一個建立在FPGA上的嵌入式軟核處理器,除了可以根據需要任意新增已經提供的外設外,使用者還可以通過定製使用者邏輯外設和定製使用者指令來實現各種應用要求。這節我們就來研究如何定製基於Avalon匯流排的使用者外設。

SOPC Builder提供了一個元件編輯器,通過這個元件編輯器我們就可以將我們自己寫的邏輯封裝成一個SOPC Builder元件了。下面,我們就以PWM實驗為例,詳細介紹一下定製基於Avalon匯流排的使用者外設的過程。

      我們要將的PWM是基於Avalon匯流排中的Avalon Memory Mapped Interface (Avalon-MM),而Avalon匯流排還有其他型別的裝置,比如Avalon Streaming Interface (Avalon-ST)、Avalon Memory Mapped Tristate Interface等等,在這裡我就不詳細敘述了,需要進一步瞭解的請參考ALTERA公司的《Avalon Interface Specifications》(mnl_avalon_spec.pdf)。

      Avalon-MM介面是記憶體對映系統下的用於主從裝置之間的讀寫的介面,下圖就是一個基於Avalon-MM的主從裝置系統。而我們這節需要做的就是下圖高亮部分。他的地位與UART,RAM Controller等模組並駕齊驅的。

clip_image002

      Avalon-MM介面有很多特點,其中最大的特點就是根據自己的需求自由選擇訊號線,不過裡面還是有一些要求的。建議大家在看本文之前,先看一遍《Avalon Interface Specifications》,這樣就能對Avalon-MM介面有一個整體的瞭解。

      下圖為Avalon-MM外設的一個結構圖,

clip_image004

      理論的就說這些,下面我們舉例來具體說明,讓大家可以更好的理解。

構建HDL

      我們這一節是PWM為例,所以首先,我們要構建一個符合Avalon-MM Slave介面規範的可以實現PWM功能的時序邏輯,在這裡,我們利用Verilog語言來編寫。在程式中會涉及到Avalon訊號,在這裡,我們說明一下這些訊號(其中,方向以從裝置為基準)

HDL中的訊號 

Avalon訊號型別 

寬度

方向

描述

clk

clk

1

input

同步時鐘訊號

reset_n

reset_n

1

input

復位訊號,低電平有效

chipselect

chipselect

1

input

片選訊號

address

address

2

input

2位地址,譯碼後確定暫存器offset

write

write

1

input

寫使能訊號

writedata

writedata

32

input

32位寫資料值

read

read

1

input

讀時能訊號

byteenable

byteenable

1

input

位元組使能訊號

readdata

readdata

32

output

32位讀資料值

此外,程式中還包括一個PWM_out訊號,這個訊號是PWM輸出,不屬於Avalon介面訊號。

      PWM內部還包括使能控制暫存器、週期設定暫存器以及佔空比設定暫存器。設計中將各暫存器對映成Avalon Slave埠地址空間內一個單獨的偏移地址。沒個暫存器都可以進行讀寫訪問,軟體可以讀回暫存器中的當前值。暫存器及偏移地址如下:

暫存器名

偏移量

訪問屬性

描述

clock_divide_reg

00

讀/寫

設定PWM輸出週期的時鐘數

duty_cycle_reg

01

讀/寫

設定一個週期內PWM輸出低電平的始終個數

control_reg

10

讀/寫

使能和關閉PWM輸出,為1時使能PWM輸出

程式如下:

?
module PWM(
    clk,
    reset_n,
    chipselect,
    address,
    write,
    writedata,
    read,
    byteenable,
    readdata,
    PWM_out);
 
input clk;
input reset_n;
input chipselect;
input [1:0]address;
input write;
input [31:0] writedata;
input read;
input [3:0] byteenable;
output [31:0] readdata;
output PWM_out;
 
reg [31:0] clock_divide_reg; 
reg [31:0] duty_cycle_reg; 
reg control_reg;
reg clock_divide_reg_selected;
reg duty_cycle_reg_selected;
reg control_reg_selected;
reg [31:0] PWM_counter;
reg [31:0] readdata;
reg PWM_out;
wire pwm_enable;
 
//地址譯碼
always @ (address)
begin
    clock_divide_reg_selected<=0;
    duty_cycle_reg_selected<=0;
    control_reg_selected<=0;
    case(address)
        2'b00:clock_divide_reg_selected<=1;
        2'b01:duty_cycle_reg_selected<=1;
        2'b10:control_reg_selected<=1;
        default:
        begin
            clock_divide_reg_selected<=0;
            duty_cycle_reg_selected<=0;
            control_reg_selected<=0;
        end
    endcase
end          
 
//寫PWM輸出週期的時鐘數暫存器
always @ (posedge clk or negedge reset_n)
begin
    if(reset_n==1'b0)
        clock_divide_reg=0;
    else
    begin
        if(write & chipselect & clock_divide_reg_selected)
        begin
            if(byteenable[0])
                clock_divide_reg[7:0]=writedata[7:0];
            if(byteenable[1])
                clock_divide_reg[15:8]=writedata[15:8];
            if(byteenable[2])
                clock_divide_reg[23:16]=writedata[23:16];
            if(byteenable[3])
                clock_divide_reg[31:24]=writedata[31:24];
        end
    end
end
 
//寫PWM週期佔空比暫存器
always @ (posedge clk or negedge reset_n)
begin
    if(reset_n==1'b0)
        duty_cycle_reg=0;
    else
    begin
        if(write & chipselect & duty_cycle_reg_selected)
        begin
            if(byteenable[0])
                duty_cycle_reg[7:0]=writedata[7:0];
            if(byteenable[1])
                duty_cycle_reg[15:8]=writedata[15:8];
            if(byteenable[2])
                duty_cycle_reg[23:16]=writedata[23:16];
            if(byteenable[3])
                duty_cycle_reg[31:24]=writedata[31:24];
        end
    end
end
 
//寫控制暫存器
always @ (posedge clk or negedge reset_n)
begin
    if(reset_n==1'b0)
        control_reg=0;
    else
    begin
        if(write & chipselect & control_reg_selected)
        begin
            if(byteenable[0])
                control_reg=writedata[0];
        end
    end
end
 
//讀暫存器
always @ (address or read or clock_divide_reg or duty_cycle_reg or control_reg or chipselect)
begin
    if(read & chipselect)
        case(address)
            2'b00:readdata<=clock_divide_reg;
            2'b01:readdata<=duty_cycle_reg;
            2'b10:readdata<=control_reg;
            default:readdata=32'h8888;
        endcase
end
 
//控制暫存器
assign pwm_enable=control_reg;
 
//PWM功能部分
always @ (posedge clk or negedge reset_n)
begin
    if(reset_n==1'b0)
        PWM_counter=0;
    else
    begin
        if(pwm_enable)
        begin
            if(PWM_counter>=clock_divide_reg)
                PWM_counter<=0;
            else
                PWM_counter<=PWM_counter+1;
        end
        else
            PWM_counter<=0;
    end
end     
 
always @ (posedge clk or negedge reset_n)
begin
    if(reset_n==1'b0)
        PWM_out<=1'b0;
    else
    begin
        if(pwm_enable)
        begin
            if(PWM_counter<=duty_cycle_reg)
                PWM_out<=1'b1;
            else
                PWM_out<=1'b0;
        end
        else
            PWM_out<=1'b0;
    end
end
 
endmodule

上面的程式儲存好以後,命名為PWM.v,並將其存放到工程目錄下。

硬體設定

      接下來,我們就通過SOPC Builder,來建立PWM模組了。首先,開啟Quartus軟體,進入SOPC Builder。進入後,點選下圖紅圈處

clip_image006

點選後,如下圖所示,點選Next,

clip_image008

點選後,如下圖所示,點選下圖紅圈處,將我們剛才建立的PWM.v加進來。(我將PWM。v放到了工程目錄下的pwm資料夾下)

clip_image010

加入後,系統會對PWM.v檔案進行分析,如下圖所示,出現紅圈處的文字,說明分析成功,點選close,關閉對話方塊。

clip_image012

然後點選Next,如下圖所示,通過下圖,我們可以看到,PWM.v中的訊號都出現在這裡面了。我們可以根據我們的功能要求來配置這些訊號,其中,Interface是Avalon介面型別 ,它包括Avalon-MM、Avalon-ST、Avalon Memory Mapped Tristate Interface等等。Signal Type指的是各個Avalon介面型別下的訊號型別。PWM.v中的訊號我們已經在前面都介紹過了,大家按照上面的要求設定就可以了。預設情況只有PWM_out需要改動,如下圖示紅圈處設定,

clip_image014

其中,Interface在下拉選單中選擇下圖紅圈處所示的選項。

clip_image016

上面的選項都設定好以後,點選Next,如下圖所示,我們通過下圖紅圈處的下拉條向下拉

clip_image018

拉到下圖所示位置停止,我們將紅圈處的改選為NATIVE,這個地方就是地址對齊的選項,我們選擇為靜態地址對齊。其他的地方都預設,不需要改動。

clip_image020

      這裡面還有很多選項,其中Timing部分需要說明一下,PWM的Avalon Slave埠與Avalon Slave埠時鐘訊號同步,讀/寫時的建立很保持時間為0,因為讀、寫暫存器僅需要一個時鐘週期,所以讀/寫時為0等待切不需要讀延時。

接著點選Next,如下圖所示,其中紅圈處需要注意,這個地方需要可以建立新組,然後在SOPC Builder中體現出來。

clip_image022

點選Finish後,會出現下面的對話方塊,點選Yes,就會生成一個PWM_hw.tcl指令碼檔案,大家可以開啟看一下,裡面放置的是剛才我們配置PWM時候的配置資訊。

clip_image024

上面都完成以後,我們回到了SOPC Builder介面,我們在左側邊欄中可以找到下圖所示的紅圈處

clip_image026

大家看到了吧,MyIP就是我們剛才建立的group。雙擊PWM,我們建立PWM模組,如下圖

clip_image028

點選Finish,完成建立。

這裡還需要設定一步,點選下圖紅圈處

clip_image030

點選後,如下圖所示,點選IP Serarch Path,然後點選Add,新增PWM.v所在位置的路徑

clip_image032

新增後,如下圖所示

clip_image034

      點選Finish完成。設定這個選項是為了讓SOPC Builder可以找到PWM.v的位置。不然就會出現下次你進入SOPC Builder的時候PWM模組無效的問題。

接下來的工作就是自動分配地址,分配中斷,編譯,等待......

      編譯好以後,我們回到Quartus軟體介面,我們可以看到,PWM出現了,我將它接到了一個LED上了,我們可以通過PWM改變LED的亮度,實現LED漸亮漸滅的過程。

clip_image036

接下來又是編譯,等待.....

      做好硬體部分工作以後,我們開啟NIOS IDE,開始軟體程式設計部分。

軟體開發

      首先對工程重新編譯一次,Ctril+B,等待......

      編譯好以後,我們來看一下system.h的變化情況,我們可以發現,多出來PWM部分了。

下面是PWM測試程式碼,

#include <unistd.h>
#include "system.h"
 
//根據暫存器的偏移量,我們定義一個結構體PWM
typedef struct{
    volatile unsigned int divi;
    volatile unsigned int duty;
    volatile unsigned int enable;
}PWM;
 
int main()
{
int dir = 1;
 
    //將pwm指向PWM_0_BASE首地址
PWM *pwm = (PWM *)PWM_0_BASE;
//對pwm進行初始化,divi最大值為232-1。
    pwm->divi = 1000;
    pwm->duty = 0;
    pwm->enable = 1;
  
    //通過不斷的改變duty值來改變LED一個週期亮燈的時間長短
    while(1){
        if(dir > 0){
            if(pwm->duty < pwm->divi)
                pwm->duty += 100;
            else
                dir = 0;
        }
        else{
            if(pwm->duty > 0)
                pwm->duty -= 100;
            else
                dir = 1;
        }
         
        usleep(100000);
    }
     
    return 0;   
}


相關推薦

FPGA黑金開發NIOSII那些事兒--基於AVALON匯流排IP定製

簡介       NIOS II是一個建立在FPGA上的嵌入式軟核處理器,除了可以根據需要任意新增已經提供的外設外,使用者還可以通過定製使用者邏輯外設和定製使用者指令來實現各種應用要求。這節我們就來研究如何定製基於Avalon匯流排的使用者外設。 SOPC Builder提供了一個元件編輯器,通過這個

FPGA黑金開發Verilog HDL那些事兒--串列埠模組

關於FPGA串列埠通訊的問題,老實說看了好多資料,都沒有找到滿意的結果,直到在黑金動力論壇中看到這篇文章,一時竟有豁然開朗之感,老實說黑金寫的文章這的很不錯,本人在裡面受益頗多,在此對黑金的工作人員表示致敬! 3.4 實驗十:串列埠模組 微控制器?串列埠?這些已經是眾所

基於springboot+redis+bootstrap+mysql開發一套屬於自己的分散式springcloud雲許可權架構許可權架構消費者完整實現

      在第十四章我們已經完成了通用業務類的編寫,因此本章我們將講解如何完整的實現我們的許可權架構的消費者的實現,首先開啟我們的rbac-consumer工程,接著開啟我們的主入口檔案RbacConsumerApplication.java加入@EnableDiscove

基於springboot+redis+bootstrap+mysql開發一套屬於自己的分散式springcloud雲許可權架構許可權架構生產者組織架構

      在第十章我們完成了對使用者管理的整合,本章我們將完成對組織架構管理的整合開發工作,首先開啟我們的rbac-produce工程,接著在com/produce/sys/dao目錄底下建立一個UserDao.java介面內容如下:package com.produce.

基於springboot+redis+bootstrap+mysql開發一套屬於自己的分散式springcloud雲許可權架構路由閘道器

      在前面十六章我們完成了註冊中心、鏈路中心、許可權架構生產者、許可權架構消費者的整合開發工作,本章將開始重點講解我們的路由閘道器的實現,由於我們的微服務內部是無許可權的,因此我們的微服務內部是不對外暴露埠的,所有的請求全部通過路由閘道器來進行請求的,因此在本章我們的

基於springboot+redis+bootstrap+mysql開發一套屬於自己的分散式springcloud雲許可權架構許可權架構消費者通用類編寫

       許可權架構的消費者和許可權架構的生產者一樣可以高度抽象化我們的通用接口出來,因此本章我們將這些消費者介面高度抽象出來,理論上這些高度抽象出來的介面是可以作為一個獨立的module需要的時候使用maven引入,不過此處就不再解耦出來,而是直接寫在我們的許可權架構服

慕課網實戰Spark Streaming實時流處理項目實戰筆記之銘文升級版

eid 實時 root 現在 ava == oop urn 啟動 銘文一級: 功能1:今天到現在為止 實戰課程 的訪問量 yyyyMMdd courseid 使用數據庫來進行存儲我們的統計結果 Spark Streaming把統計結果寫入到數據庫裏面 可視化前端根據:yyy

精編重製版JavaWeb 入門級專案實戰 -- 文章釋出系統 第一節

說明 本教程是,原文章釋出系統教程的精編重置版,會包含每一節的原始碼,以及修正之前的一些錯誤。因為之前的教程只做到了評論模組,很多地方還不完美,因此重製版會修復之前的一些謬誤和闡述不清的地方,而且,後期我會考慮完成該專案的後臺管理。希望本教程提供的內容,可以成為JavaWeb初學者一套較為完整的練手專案。

git之窗線上問題如何拉取緊急分支

一、前提       通常使用git,都會在上線前把程式碼合併到master分支,在master上打好tag,由上線tag、回退tag確保上線正常。       例如:       上線tag: V

零基礎Python3學習課後練習題

本文是跟著魚C論壇小甲魚零基礎學習Python3的視訊學習的,課後題也是跟隨每一課所附屬的題目來做的,根據自己的理解和標準答案記錄的筆記。 第十九課 測試題: 0.下邊程式會輸出什麼? def next():       print('我在next()函式裡.

Python小白學習之路內建函式二

序列操作類函式 all() 功能:判斷可迭代物件的每個元素是否都為True值注意:If the iterable is empty, return True.(舉例3) 回顧:None     ''      ()     {}       []    0 ==>False其餘 ==>Tru

SpringMVC非同步傳送表單資料到JavaBean,並響應JSON文字返回

  1) 提交表單後,將JavaBean資訊以JSON文字形式返回到瀏覽器 bean2json.jsp <form> 編號:<input type="text" name="id"

一步一個腳印Tomcat+MySQL為自己的APP打造伺服器3-1Android 和 Service 的互動之GET方式

      好久沒更新了,罪過罪過。最對不起的人莫過於那些支援和等待在下拙文的諸位,在此道一聲抱歉。管窺之見,僥倖博得各位認同,給了我莫大的鼓勵。       話休絮煩,文接前章。       到【一步一個腳印】Tomcat+MySQL為自己的APP打造伺服器(2-3)Se

LeetCode題目記錄-11判斷二叉樹是否是映象的對稱的

Symmetric Tree Given a binary tree, check whether it is a mirror of itself (ie, symmetric around it

一步一個腳印Tomcat+MySQL為自己的APP打造伺服器3-2Android 和 Service 的互動之POST方式

        今天是聖誕節,雖說我本人對這個西方節日沒什麼感覺,但畢竟還是有很多小年輕人(自認為已然脫離年輕人的航道)挺在意這個節日的,在這裡祝大家聖誕快樂吧(要是湊巧你也沒什麼感覺,那就預祝元旦快

小白學PyTorch6 模型的構建訪問遍歷儲存附程式碼

文章轉載自微信公眾號:機器學習煉丹術。歡迎大家關注,這是我的學習分享公眾號,100+原創乾貨。 文章目錄: [TOC] 本文是對一些函式的學習。函式主要包括下面四個方便: - 模型構建的函式:```add_module```,```add_module```,```add_module``` - 訪問子

pytest官方文件解讀fixtures - 8. yield和addfinalizer的區別填坑

在[上一章](https://www.cnblogs.com/pingguo-softwaretesting/p/14479170.html)中,文末留下了一個坑待填補,疑問是這樣的: 目前從官方文件中看到的是 ``` We have to be careful though, because pytest

Android開發系列:讀取assets文件夾下的數據庫文件

pack 取數 code ada tracking 編寫 數據庫 sdn where 在做Android應用的時候,不可避免要用到數據庫。可是當我們把應用的apk部署到真機上的時候,已經創建好的數據庫及其裏邊的數據是不能隨著apk一起安裝到真機上的。 (PS:這篇

Vue模塊化開發

pat center clas 發生 ont mar tex () 原型 模塊化開發   使用vue-cli創建項目 1. vue-router模塊化 引入vue-router cnpm install vue-router -S 1.1 編輯main.js

QT開發——QWT簡介

Qt QWTQT開發——QWT簡介 一、QWT簡介 QWT,即Qt Widgets for Technical Applications,是一個基於LGPL版權協議的開源項目, 可生成各種統計圖,是為具有技術專業背景的程序提供GUI組件和一組實用類,其目標是以基於2D方式的窗體部件來顯示數據, 數據源以數值,