在Avalon总线协议(一)和Avalon总线协议(二)中大概了解Avalon总线的几种类型,目前比较常用到的就是Avalon-MM接口了,虽然在概念中有那么多的属性,但是具体使用起来还是非常简单的。
前面提到过Avalon总线常用于 用户自定义的逻辑 与 NIOS Ⅱ处理器 之间进行通信,再通俗一点的理解就是硬件(Verilog代码)和软件(Nios Ⅱ处理器)进行数据交互。Nios Ⅱ作为主端口(Master),而Verilog代码模块实现了从端口(Slave),比如在Verilog代码中写了一个计数器,而Nios想要知道这个计数器的值,那么就可以在Verilog代码中定义一个寄存器,该寄存器具有与之相对应的地址,Nios Ⅱ可以根据地址对这个寄存器的值进行读取或者写入,这就是Avalon-MM协议。
如图,Verilog代码中定义了三个寄存器,分别是REG1、REG2、REG3,偏移量分别是OFFSET1、OFFSET2、OFFSET3;
当这一部分Verilog代码作为自定义组件加入到NIOS中并进行编译后,会自动产生BASE;
这时候寄存器的地址和偏移量就都有了,那么NIOS就可以对寄存器中的数据进行读写,从而实现通信!
在NIOS中有一些已经定义好的函数方便去对数据进行操作
最常用的肯定是IORD()、IOWR()以及对PIO操作的IORD_ALTERA_AVALON_PIO_DATA()、IOWR_ALTERA_AVALON_PIO_DATA()
其实都一样>-<,IORD_ALTERA_AVALON_PIO_DATA()还是调用的IORD()
- IORD(BASE, OFFSET)
- //BASE为寄存器的基地址,OFFSET为寄存器的偏移量
- //从基地址为BASE的设备中读取寄存器中偏移量为OFFSET的单元里面的值
-
- IOWR(BASE, OFFSET, DATA)
- //BASE为寄存器的基地址,OFFSET为寄存器的偏移量,DATA为要写入的数据
- //向基地址为BASE的设备偏移量为OFFSET寄存器中写入数据DATA
-
- IORD_ALTERA_AVALON_PIO_DATA(BASE)
- //BASE为寄存器的基地址
- //向基地址为BASE的设备中读取数
-
- IOWR_ALTERA_AVALON_PIO_DATA(BASE, DATA)
- //BASE为寄存器的基地址,DATA为要写入的数据
- //向基地址为BASE的设备中写入数据DATA
其他NIOS函数可以参考:NIOS常用函数详解-CSDN博客
其实已经在前面的文章中实现过了,只不过没有较为详细的解释:
SOPC之NIOS Ⅱ实现电机转速PID控制_STATEABC的博客-CSDN博客
就用其中的电机PWM控制模块作为例子
- module MOTOR_PWM(
- input clk,
- input reset_n,
- //Avalon-MM输入输出
- input avalon_cs, // 片选信号,进行数据操作时自动置为1
- input [2:0] avalon_address, // 基地址,位宽根据要定义的寄存器个数
- input avalon_write, // 写入信号
- input [31:0] avalon_writedata, // 写入数据
- input avalon_read, // 读取信号
- output reg [31:0] avalon_readdata, // 读取数据
-
- input signed [31:0] Speed,
- output reg PWM,
- output reg IN1,
- output reg IN2
- );
-
- reg pwm_tem;
- reg [31:0] total; // 总时间
- reg [31:0] high; // 高位时间
- reg [31:0] count; // 计数器
-
- /
- // 定义寄存器的偏移量,两种写法都可以
- localparam REGISTER_TOTAL_DUR = 0;
-
- `define REGISTER_HIGH_DUR 2'd1
-
-
- /
- // Avalon-MM通信
- always @(posedge clock or negedge reset_n)
- begin
- if (~reset_n)
- begin
- high <= 0;
- total <= 0;
- end
- // 当片选信号和写入信号有效,反映在软件上就是执行了IOWR()
- else if (avalon_cs & avalon_write)
- begin
- if (avalon_address == REGISTER_TOTAL_DUR) // 当地址等于0,即REGISTER_TOTAL_DUR
- total <= avalon_writedata; // avalon_writedata为IOWR()写入的DATA值
- else if (avalon_address == `REGISTER_HIGH_DUR)
- high <= avalon_writedata;
- end
- // 当片选信号和读取信号有效,反映在软件上就是执行了IORD()
- else if (select_cs & select_read)
- begin
- if (avalon_address == `REGISTER_TOTAL_DUR) // 当地址等于0,即REGISTER_TOTAL_DUR
- select_readdata <= total; // avalon_writedata为IORD()的返回值
- else if (avalon_address == `REGISTER_HIGH_DUR)
- select_readdata <= high;
- end
- end
-
- /
- // 进行PWM输出
- always @(*)
- begin
- if (Speed>0) begin
- {IN1, IN2, PWM} <= {1'b1, 1'b0, pwm_tem};
- end
- else begin
- {IN1, IN2, PWM} <= {1'b1, 1'b0, pwm_tem};
- end
- end
-
- always @(posedge clock or negedge reset_n)
- begin
- if (~reset_n)
- begin
- count <= 1;
- end
- else if (count >= total)
- begin
- count <= 1;
- end
- else
- count <= count + 1;
- end
-
- always @(posedge clock)
- begin
- pwm_tem <= (count <= high) ? 1'b1 : 1'b0;
- end
-
- endmodule
将其作为自定义组件加入NIOS系统中并进行编译
硬件部分进行全编译后,生成的systm.h文件中会包含其BASE信息
然后就可以根据BASE和OFFSET进行数据的读取
- #include
- #include
- #include "system.h"
- #include "altera_avalon_pio_regs.h" //IOWR_ALTERA_AVALON_PIO_DATA
-
- int main()
- {
- int high,total;
- //写入数据
- IOWR(MOTOR_PWM_BASE,0,2000);
- IOWR(MOTOR_PWM_BASE,1,1000);
- //读取数据
- total = IORD(MOTOR_PWM_BASE,0);
- high = IORD(MOTOR_PWM_BASE,1);
-
- printf("total= %d\r\n", total);
- printf("high = %d\r\n", hight);
-
- return 0;
- }