原创 SOPC系统自定义外设

2010-6-3 19:27 3095 6 6 分类: FPGA/CPLD

//***********************************************************************


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<5000begin 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的灵活性!


//***********************************************************************

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条