zoukankan      html  css  js  c++  java
  • 学会自己定制简单的IP

    学习了很长时间的avolon 总线无果,迷茫中,很难搞清楚到底怎么学习了IP的构建,先马马虎虎的跟着别人做做试试

    在定制之前,得打开之前的工程文件夹看看你之前的nios II工程,比如做了LED显示的时候,用到了PIO的IP,找到了LED4.V(SOPC中定制硬件时候起的名字),打开后看到了下面的代码部分

    module led4 (

    // inputs:

    address,

    chipselect,

    clk,

    reset_n,

    write_n,

    writedata,



    // outputs:

    out_port,

    readdata

    )

    ;



    output [ 3: 0] out_port;

    output [ 3: 0] readdata;

    input [ 1: 0] address;

    input chipselect;

    input clk;

    input reset_n;

    input write_n;

    input [ 3: 0] writedata;



    wire clk_en;

    reg [ 3: 0] data_out;

    wire [ 3: 0] out_port;

    wire [ 3: 0] read_mux_out;

    wire [ 3: 0] readdata;

    assign clk_en = 1;

    //s1, which is an e_avalon_slave

    assign read_mux_out = {4 {(address == 0)}} & data_out;

    always @(posedge clk or negedge reset_n)

    begin

    if (reset_n == 0)

    data_out <= 0;

    else if (chipselect && ~write_n && (address == 0))

    data_out <= writedata[3 : 0];

    end





    assign readdata = read_mux_out;

    assign out_port = data_out;



    endmodule


     这是在设置了SOPC以后,generate出来的关于PIO IP核的verilog代码,所以算是一个官方的IP核,代码并不是很好读。但是其风格应该是我们模仿的不二选,因为毕竟是QUARTUS II自己生成的代码。

    对照avalon 总线时序,应该明白这是最简单的基本读写时序。当然,这个程序的地址译码和寄存器的操作部分是在一起的。所以并不是很好读,也许是过于简单的原因吧,没有用到strobe的变量。但是再打开复杂一点的程序,就会发现有strobe这个单词了。它就是闸门的意思,如在我的外部中断生成的IP中,就有这么一句:

    edge_capture_wr_strobe = chipselect && ~write_n && (address == 3);

    这中间就有了strobe。这就是一个门,门的钥匙就是address == 3。这个门是通向了写edge_capture这个房间的。这样我们就明白了很多吧,对于address的理解是对哪个寄存器进行操作的。所以在很多资源中,我们都看到了把address也叫做offset.这个offset至关重要,如果知道在做工程的时候在system.h文件中的**_BASE算最重要的话,那这个offset应该在软硬件里面必须是第二重要的吧,因为这个offset就是在BASE的基础上的偏倚对寄存器的操作。我们如果对寄存器的操作还只是停留在使用

    #include "altera_avalon_pio_regs.h"  的话,那现在我们就可以更进一步的观察了。试着找到这个altera_avalon_pio_regs.h。

    这个.H文件的路径非常的深。我从

    C:\altera\90\ip\altera\sopc_builder_ip\altera_avalon_pio\inc

    这个路径下把它挖了出来,看到了这样一句定义:

    #define IOWR_ALTERA_AVALON_PIO_DATA(base, data)      IOWR(base, 0, data)

    原来他也只是一句宏定义,并不是一个函数什么的。但是们看到后面的一句IOWR(base, 0, data时就又生好奇,继续把这句话的来源揪出来。

    这个路径依然很深

    C: \altera\90\nios2eds\components\altera_nios2\HAL\inc 

    我们再看到这样一句定义:

    #define IOWR(BASE, REGNUM, DATA)  

      __builtin_stwio (__IO_CALC_ADDRESS_NATIVE ((BASE), (REGNUM)), (DATA)) 

      这个(REGNUM)就是我们的offset,也就是address。其实不必知道这么多,只要理解IOWR(base, 0, data)的参数0并不是一个随便的数字,它在硬件上对应的含义就是我们定义的address。那一切都明白的差不多了。

    但是这个程序中只有一个寄存器,完成读写任务只需要一个offset,为什么这里定义了两位的addres,就不懂了。

    知道了这些,就可以读懂了别人的IP了,当然也可以自己马上尝试自己构建IP。

    下面是我自己的构建的IP实现PWM输出的功能,当然这个代码已经有了很多版本,我也看过了黑金发布的。我这里主要是模仿了quartus II的风格,代码如下:

    module pwm (
    //inputs
    clk,
    reset_n,
    chipselect,
    address,
    write_n,
    writedata,
    readdata,
    //outputs
    PWM_out
    )
    ;

    input clk;
    input reset_n;
    input chipselect;
    input [1:0]address;
    input write_n;
    input [31:0]writedata;
    output [31:0]readdata;
    output PWM_out;

    reg [31:0] PWM_period_reg;
    reg [31:0] PWM_duty_reg;
    wire [31:0] readdata;
    reg PWM_enable;
    reg PWM_out_reg;
    wire PWM_period_wr_strobe;
    wire PWM_duty_wr_strobe;
    wire PWM_enable_wr_strobe;
    wire [31:0] read_mux_out;
    wire PWM_control;
    reg [31:0] PWM_counter;
    //read register
    assign read_mux_out=({32{(address==0)}}&PWM_period_reg|
    {32{(address==1)}}&PWM_duty_reg|
    {32{(address==2)}}&PWM_enable);

    assign readdata = read_mux_out;



    //operate PWM_period_reg
    assign PWM_period_wr_strobe = chipselect && ~write_n && (address == 0);
    always @(posedge clk or negedge reset_n)
    begin
    if (reset_n == 0)
    PWM_period_reg=0;
    else if (PWM_period_wr_strobe)
    PWM_period_reg<=writedata;
    end

    //operate PWM_duty_reg
    assign PWM_duty_wr_strobe= chipselect && ~write_n && (address == 1);
    always @(posedge clk or negedge reset_n)
    begin
    if (reset_n == 0)
    PWM_duty_reg<=0;
    else if (PWM_duty_wr_strobe)
    PWM_duty_reg<=writedata;
    end

    //operate PWM_enable_reg
    assign PWM_enable_wr_strobe= chipselect && ~write_n && (address == 2);
    always @(posedge clk or negedge reset_n)
    begin
    if (reset_n == 0)
    PWM_enable<=0;
    else if (PWM_enable_wr_strobe)
    PWM_enable=writedata[0];
    end
    //achieve PWM function

    //counter
    assign PWM_control=PWM_enable;
    always @(posedge clk or negedge reset_n)
    begin
    if (reset_n == 0)
    PWM_counter<=0;
    else
    if(PWM_control)
    if(PWM_counter<PWM_period_reg)
    PWM_counter<=PWM_counter+32'd1;
    else
    PWM_counter<=0;
    end


    //PWM output
    always @(posedge clk or negedge reset_n)
    begin
    if (reset_n == 0)
    PWM_out_reg<=0;
    else
    if(PWM_control)
    begin
    if(PWM_counter<PWM_duty_reg)
    PWM_out_reg<=1'b1;
    else PWM_out_reg<=1'b0;
    end
    else PWM_out_reg<=1'b0;
    end
    assign PWM_out=PWM_out_reg;
    endmodule

    当然IP定制完成后是需要进行modelsim的仿真的。前面一篇博客发布了一点关于testbench的认识的原因。工欲善其事必先利其器,设计到了这步,还想靠quartus II自带的仿真工具来验证是很难的。

    定制了nios II 软核后我们需要在nios II中自己编写HAL文件。

    我得提出来为了对比效果我在自己的SOPC搭建的工程中不只使用了PWM的输出,还使用了一个两位宽的PIO。因为我是用LED来观察我得实验效果的,当然没有示波器那么专业。

    在nios II中建立工程后,首先是HAL文件的设计,我在自己的PWM.h文件中声明了以下内容

    #ifndef PWM_H_

    #define PWM_H_



    #include <io.h>

    #define IO_WR_PWM_PERIOD(data) IOWR(PWM_BASE,0,data)

    #define IO_WR_PWM_DUTY(data) IOWR(PWM_BASE,1,data)

    #define PWM_enable IOWR(PWM_BASE,2,1)

    #define PWM_disble IOWR(PWM_BASE,2,0)



    void PWM_configuration(alt_32 period,alt_32 duty );

    #endif /*PWM_H_*/

    这里的#include <io.h>一定是要记得加进去的,否则无法编译。至于后面的一些一句的来历我不想再赘述。

    然后是PWM.c文件。很简单的几行代码。

    #include "system.h"

    #include "alt_types.h"

    #include "../inc/pwm.h"





    void PWM_configuration(alt_32 period,alt_32 duty )

    {

    IO_WR_PWM_PERIOD(period);

    IO_WR_PWM_DUTY(duty);

    }


     

    到此我们的设计很容易看懂了,接下来就是main.c了。

    #include "system.h"
    #include "alt_types.h"
    #include "unistd.h"
    #include "altera_avalon_pio_regs.h"
    #include "../inc/pwm.h"

    void delay(alt_u32 i);

    int main (void) __attribute__ ((weak, alias ("alt_main")));

    int alt_main()
    {
    PWM_configuration(50000000,10000000);//50 000 000,10 000 0000
    PWM_enable;
    while(1)
    {
    IOWR_ALTERA_AVALON_PIO_DATA(LED2_BASE, 0);
    delay(500000);
    IOWR_ALTERA_AVALON_PIO_DATA(LED2_BASE, 3);
    delay(500000);
    }
    }



    void delay(alt_u32 i)
    {
    for(;i>0;i--);
    }

    以上程序的PWM波形输出是按照1Hz,1/5占空比设计的,所以是可以看到LED闪烁的。

     编译下载,OK,可以看到明显的效果,三个LED闪烁起来,而且频率也是不一样的。因为手头没有示波器,我也没有用示波器来测量频率和占空比。
    到此简单的IP设计思想和代码为止。
    引用请声明来源:http://www.cnblogs.com/michile/admin/EditPosts.aspx   by  michile
  • 相关阅读:
    P2639 [USACO09OCT]Bessie的体重问题Bessie's We…
    P2871 [USACO07DEC]手链Charm Bracelet
    P1983 车站分级
    P1038 神经网络
    P1991 无线通讯网
    P1546 最短网络 Agri-Net
    P1197 [JSOI2008]星球大战
    P1004 方格取数
    P1111 修复公路
    pd_ds 之 hash
  • 原文地址:https://www.cnblogs.com/michile/p/2263086.html
Copyright © 2011-2022 走看看