//***********************************************************************
SOPC系统自定义外设:硬件设计,软件设计,ver8.1
//***********************************************************************
硬件设计
http://www.cnblogs.com/nick123/archive/2009/05/09/1453325.html
开发环境:Quartus II 8.1 + Nios II IDE 8.1
外设功能:4*4键盘
自定义外设是SOPC系统灵活性的重要体现,是SOPC系统中极其重要的一种设计方法。在大量的数据需要处理时,利用自定义外设由具体的硬件来实现,可以极大程度的提高系统运行的速度,同时便于系统的模块化与集成,是SOPC系统设计的重中之重!!!
在设计自定义外设之前,一定要知道外围电路的工作原理,也就是自定义的外设的时序、逻辑一定要根据具体的硬件电路来设计。其原图在前一篇Verilog HDL 4*4矩阵 键盘扫描程序中。
键盘是嵌入式系统中应用最广的输入设备,这里要设计的就是4*4键盘的硬件扫描电路,可以通过读相关的寄存器获取键值,值得一提的是该自定义外设支持中断的方式获取键值!!!
自定义外设可以为Master和Slave两种,这里设计的是一个只含有Slave的外设,Verilog HDL源程序如下,主要是在上一篇文章的基础上修改而来。主要添加了Avalon Slave Port的接口部分:也就是和Avalon交换架构进行交互的一些信号的集合(这只是最基本的传输,不包含任何高级传输属性)。
1 //Write by Nick 2009.5.9
2 module key
3 (
4 //Avalon MM slave port interface "s1" with irq
5 clk, //50MHZ
6 reset_n,
7
8 chipselect,
9 address,
10 write,
11 writedata,
12 readdata,
13 irq,
14 row,
15 col
16 );
17 input clk;
18 input reset_n;
19
20 input chipselect;
21 input [1:0] address;
22 input write;
23 input [7:0] writedata;
24 output [7:0] readdata;
25 input [3:0] row;
26 output irq;
27 output [3:0] col;
28
29 reg [7:0] readdata;
30 reg irq;
31 reg irq_reg;
32 reg [3:0] col;
33 reg [4:0] key_value;
34
35 reg [12:0] count;//delay
36 reg [2:0] state;
37 reg [2:0] pre_state;
38 reg key_flag;
39 reg [3:0] col_reg;
40 reg [3:0] row_reg;
41 wire [7:0] irq_capture;
42 wire [7:0] readdata_reg;
43
44 assign readdata_reg={8{(address==0)}}&{4'b0000,key_value}|
45 ({8 {(address == 1)}} & irq_capture);
46
47 assign irq_capture=key_value;
48
49 always @(posedge clk)
50 if(!reset_n) begin readdata<=8'h00;irq<=1'b0;end
51 else
52 begin
53 readdata<=readdata_reg;
54 if(chipselect&write&&(address==2)) irq<=1'b0;//clear irq signal
55 else if(key_flag==1'b1) irq<=1;
56 end
57
58
59 always @(posedge clk or negedge reset_n)
60 if(!reset_n) begin col<=4'b0000;state<=0;pre_state<=0;count<=0;end
61 else
62 begin
63 case (state)
64 0:
65 begin
66 col[3:0]<=4'b0000;
67 key_flag<=1'b0;
68 pre_state<=0;
69 if(row[3:0]!=4'b1111) state<=2;
70 else begin state<=0;end
71 end
72 1:
73 begin
74 if(count<5000) begin count<=count+1;state<=1;end
75 else
76 begin
77 count<=0;
78 if(row[3:0]!=4'b1111) state<=6;
79 else state<=pre_state+1;
80 end
81 end
82
83 2:
84 begin
85 col[3:0]<=4'b1110;
86 state<=1;
87 pre_state<=2;
88 end
89 3:
90 begin
91 col[3:0]<=4'b1101;
92 state<=1;
93 pre_state<=3;
94 end
95 4:
96 begin
97 col[3:0]<=4'b1011;
98 state<=1;
99 pre_state<=4;
100 end
101 5:
102 begin
103 col[3:0]<=4'b0111;
104 state<=1;
105 pre_state<=5;
106 end
107
108 6:
109 begin
110 if(row[3:0]!=4'b1111)
111 begin
112 col_reg<=col;
113 row_reg<=row;
114 state<=7;
115 key_flag<=1'b1;
116 end
117 else
118 begin state<=0;end
119 end
120 7:
121 begin
122 key_flag<=1'b0;
123 if(row[3:0]!=4'b1111) state<=7;
124 else state<=0;
125 end
126 endcase
127 end
128
129
130 always @(clk or col_reg or row_reg)
131 begin
132 if(!reset_n) key_value<=0;
133 else
134 if(key_flag==1'b1)
135 begin
136 case ({col_reg,row_reg})
137 8'b1110_1110:key_value<=1;
138 8'b1110_1101:key_value<=2;
139 8'b1110_1011:key_value<=3;
140 8'b1110_0111:key_value<=4;
141
142 8'b1101_1110:key_value<=5;
143 8'b1101_1101:key_value<=6;
144 8'b1101_1011:key_value<=7;
145 8'b1101_0111:key_value<=8;
146
147 8'b1011_1110:key_value<=9;
148 8'b1011_1101:key_value<=10;
149 8'b1011_1011:key_value<=11;
150 8'b1011_0111:key_value<=12;
151
152 8'b0111_1110:key_value<=13;
153 8'b0111_1101:key_value<=14;
154 8'b0111_1011:key_value<=15;
155 8'b0111_0111:key_value<=16;
156 endcase
157 end
158 end
159 endmodule
160
下面介绍如何将自定义外设添加到SOPC系统。
打开SOPC Builder,添加Verilog HDL文件。如下图:
设置信号类型如下:
设置Avalon接口如下:
添加自定义组件的一些基本信息:
这样就完成了自定义组件的添加,保存后退出即可在SOPC Builder 组件栏看到刚添加的组件。
<?XML:NAMESPACE PREFIX = O />
点“Add”即可像添加其它IP一样添加到SOPC系统中来。
可以清楚的看到键盘中断优先级为“2”,Nios II系统中断号越低,中断优先级越高。点击“Generate”生成Nios II系统如下,可以看到组件已经成功添加到顶层文件.
绑定管脚后编译,即完成了自定义外设的整个硬件设计,接下来就是软件设计与验证。
//***********************************************************************
软件设计
http://www.cnblogs.com/nick123/archive/2009/05/09/1453422.html
完成自定义外设的硬件设计后,就需要编写软件来测试外设的设计是否正确了。
在这之前首先要弄清楚Nios II中的地址对齐,对Avalon slave来说,有两种地址对齐方式:动态地址对齐和静态地址对齐。
动态地址对齐:动态地址对齐可以自动适应和Avalon master端口宽度不同的器件,而同时保持地址增长的方式是以字节为单位增长的方式。匹配不同端口宽度的master和slave时使用动态地址对齐方式可以得到一个连续的存贮器空间。但动态地址对齐在读操作的时候有附作用。当一个32位Nios II core读一个8位宽的slave时,物理会产生4次8位的读操作,而读一个16位宽的slave时,则要产生2次的读操作。大部分寄存器类型的外设不能容忍这种附作用,所以动态地址对齐一般不适合用于寄存器外设,主要用于存贮器。如果外部存贮器的宽度大于8位时,比如16位或32位,则必然有字节使能信号,以便进行字节粒度的写操作。所以在为这些存贮器做接口的时候,如果采用动态地址对齐的方式,则一定要连接字节使能信号。
静态地址对齐:静态地址对齐的地址增长单位是Avalon master的端口宽度,每次读写都只对应一次操作没有什么附作用。但在匹配不同端口宽度的master和slave时,地址不能自动调整,某些地址没有相应的物理实体和它对应。当一个32位的Nios II core读一个8位宽的slave时,其获得的32位数据低8位从slave获取,而高24位则没有定义。同样,当它读16宽的slave时,其获得的32位数据低16位从slave获取,而高16位则没有定义。当Nios II core想继续读下一个8位(或16位)时,则需要增长字节地址4。除非你一定需要一个连续的地址空间,否则使用静态地址对齐方式是比较保险的方式。
在刚调试期间遇到这样一个问题:用IOWR(KEYBOARD_BASE,2,0);清楚irq中断信号,在用SignalTap II逻辑分析仪始终抓不到write信号,一直保持低电平,后来用IOWR(KEYBOARD_BASE,0,0)就能抓到write信号了。我想一定是地址对齐的问题,然后打开SOPC Builder,准备地址对齐对齐方式,可是始终没有看到那一项,以前6.0版本是有这个选项的。折腾了我一天,后来只好硬着头皮看Altera官方文档,终于找到答案了。
于是决定找到源文件,改代码,在生成的key_hw.tcl中
#set_interface_property avalon_slave addressAlignment DYNAMIC
set_interface_property avalon_slave addressAlignment NATIVE
注释掉DYNAMIC,改成NATIVE。
重新编译,问题就不服存在了,用SignalTap II成功到捕捉write信号
测试源程序如下:
1 #include "system.h"
2 #include <stdio.h>
3 #include <io.h>
4 #include "alt_types.h"
5 #include "sys/alt_irq.h"
6 static void key_isr(void* context, alt_u32 id);
7 volatile int irq_capture;
8 int main (void) __attribute__ ((weak, alias ("alt_main")));
9 int alt_main (void)
10 {
11 alt_irq_init(ALT_IRQ_BASE); //使能中断
12 alt_irq_register(KEYBOARD_IRQ,NULL , key_isr);//注册中断服务程序
13 while (1);
14 return 0;
15 }
16 static void key_isr(void* context, alt_u32 id)
17 {
18 irq_capture = IORD(KEYBOARD_BASE,1);
19 IOWR(KEYBOARD_BASE,2,0);
20 printf("key_value:%d\n",irq_capture);
21 }
22
为了能弄清楚中断的工作流程,决定进行单步调试。但必须先知道中断时如何控制和执行的。
中断由三个寄存器控制:state,ienable,ipending。
state:最后一位PIE,中断开关,1:中断使能。0:禁止外部中断
ipending:32位,第n位为1则表示正在处理第n个中断。
ienable:32位,每1位对应1个外部中断源的使能位,若第n位为1,则使能对应的中断,为0则禁止对应的中断。
注意,ienable变成了0x4,联想到上一篇(原创)SOPC系统自定义外设之:硬件设计,中断号为2,表示键盘中断已经使能。
还有一个就是终端向量表alt_irq,一共有32个,每一个对应一个中断优先级,如果中断注册成功,就会向中断向量表里面写入ISR和context
如上图,alt_irq[2]的handler指向key_isr()中断服务程序,中断注册成功!
Context为全0,因为程序在注册中断服务程序时设置的context参数为NULL。
alt_irq_active是一个32位的全局变量,当某个中断被使能后,对应位就为1。
开始硬件上运行程序,让程序执行key_isr()中断服务程序。在按键时,输出如下:
中断服务程序成功执行!!
经过这次自定义外设软硬件的调试,实在是让人感叹SOPC的灵活性!
//***********************************************************************
文章评论(0条评论)
登录后参与讨论