(原创)LEON3入门教程(四):基于AMBA APB总线的七段数码管IP核设计
摘要:这一小节将介绍下如何设计用户自定义的APB IP,并将IP嵌入到SOPC中去。一个APB IP核的主要分为三个部分:逻辑单元、寄存器单元和接口单元。所设计的IP是一个简单的七段数码管显示IP,只有一个寄存器ledindata_reg,实现数码管显示,比较简单实用,可以类比到更多的寄存器设计中。IP设计后,对其进行仿真测试和软件测试,验证其功能。该IP没有中断功能,如果需要添加中断请参考AMBA协议。
更多更新请关注我的博客:@超群天晴 http://www.cnblogs.com/surpassal/
相关阅读
(原创)LEON3入门教程(一):什么是LEON3?需要哪些开发工具和软件?
(原创)LEON3入门教程(二):Cygwin和GRtools的安装与配置
(原创)LEON3入门教程(三):基于LEON3的SOPC设计:HELLOWORLD和流水灯
一、AMBA APB总线和APB IP核
ARM研发的AMBA(Advanced Microcontroller Bus Architecture)提供一种特殊的机制,可将RISC处理器集成在其它IP芯核和外设中,2.0版AMBA标准定义了三组总线:AHB(AMBA高性能总线)、ASB(AMBA系统总线)、和APB(AMBA外设总线)。3.0版本将支持AXI。APB总线用于提供低带宽的接口用作外围总线,适合挂接一般的外围设备。
图1 是一个简单的APB总线读操作的时序图。图中第一个时钟周期是SETUP周期,第二个时钟周期是ENABLE周期,地址、控制信号在两个周期内都是有效的。PENABLE信号在传输结束的时候将被置无效。PWRITE信号维持不变,直到不第同的读写传输发生的时候改变。写操作的时序基本相同,只是PWDATA数据信号现在SETUP周期内和地址、控制信号一同给出。
图1 APB总线的简单读操作时序
一个APB IP核的主要分为三个部分:逻辑单元、寄存器单元和接口单元。其中逻辑单元完成任务逻辑的实现。对于七段数码管来说,就是完成对输入数值的显示;接口单元完成对AMBA APB总线协议的支持,包括读写协议、时序等;寄存器单元完成地址和寄存器的衍射,将AMBA APB上的地址空间对应到需要的寄存器上,这样CPU对这些地址的操作,也就完成了对相应寄存器的操作,最终实现对逻辑单元的输入输出控制等。一个APB IP的结构框图如图2所示。
图 2 一个APB IP核的结构框图
二、 IP核的设计和测试
1 逻辑设计
由于DE2-70上的七段数码管是共阳的,所以设计的是BCD输码入的共阳七段数码管。实现代码如下。
1 --超群天晴 @ NEU 2 library ieee; 3 use ieee.std_logic_1164.all; 4 use ieee.std_logic_unsigned.all; 5 6 ENTITY led IS 7 PORT( 8 indata:IN STD_LOGIC_VECTOR (3 DOWNTO 0); 9 outdata:OUT STD_LOGIC_VECTOR (7 DOWNTO 0) 10 ); 11 END led; 12 13 ARCHITECTURE rtl OF led IS 14 BEGIN 15 process(indata)-- 16 begin 17 18 --共阳 19 CASE indata IS---26 27 28 29---0000EDCB --100509 20 WHEN "0000"=>outdata<="11000000";--0 21 WHEN "0001"=>outdata<="11111001";--1 22 WHEN "0010"=>outdata<="10100100";--2 23 WHEN "0011"=>outdata<="10110000";--3 24 WHEN "0100"=>outdata<="10011001";--4 25 WHEN "0101"=>outdata<="10010010";--5 26 WHEN "0110"=>outdata<="10000010";--6 27 WHEN "0111"=>outdata<="11111000";--7 28 WHEN "1000"=>outdata<="10000000";--8 29 WHEN "1001"=>outdata<="10010000";--9 30 WHEN "1010"=>outdata<="10001000";--A 31 WHEN "1011"=>outdata<="10000011";--b 32 WHEN "1100"=>outdata<="11000110";--C 33 WHEN "1101"=>outdata<="10100001";--D 34 WHEN "1110"=>outdata<="10000110";--E 35 WHEN "1111"=>outdata<="10001110";--F 36 when others=>outdata<="11000000";--0;--0 37 END CASE; 38 END process; 39 end;
2 AMBA APB总线接口和寄存器设计
由于只需要对这个IP进行读操作,也不需要添加中断等其他功能,故只需要一个寄存器来存放输入数据即可。这里定义ledindata_reg,是对于IP基地址偏移为0(也就是基地址)的寄存器,作为处理器写数据的输入。
1 library ieee; 2 use ieee.std_logic_1164.all; 3 library grlib; 4 use grlib.amba.all; 5 use grlib.stdlib.all; 6 use grlib.devices.all; 7 8 9 entity apbseg is 10 generic ( 11 pindex : integer := 1 ;-- slave bus index 12 paddr : integer := 16#240#;-- slave address 13 pmask : integer := 16#fff#; --choose the minimun size 256byte 14 pirq : integer := 0 ---interrupt index 15 ); 16 port ( 17 rst : in std_ulogic; 18 clk : in std_ulogic; 19 apbi : in apb_slv_in_type; -- APB slave inputs 20 apbo : out apb_slv_out_type; -- APB slave outputs 21 led_out : out std_logic_vector(7 downto 0) 22 ); 23 end entity; 24 25 architecture rtl of apbseg is 26 27 component led 28 port( 29 indata:in std_logic_vector (3 downto 0); 30 outdata:out std_logic_vector (7 downto 0) 31 ); 32 end component; 33 34 constant REVISION : integer := 0; 35 constant pconfig : apb_config_type := 36 ( 37 0 => ahb_device_reg ( VENDOR_OPENCHIP, OPENCHIP_APBGPIO, 0, REVISION, pirq), 38 1 => apb_iobar(paddr, pmask) 39 ); 40 41 signal ledindata_reg :std_logic_vector(3 downto 0); 42 signal apb_write_reg :std_logic_vector(3 downto 0); 43 44 begin 45 46 --the main process 47 p:process(rst, apbi) 48 49 --variable rdata : std_logic_vector(31 downto 0) := (others => '0'); 50 variable write_temp: std_logic_vector(31 downto 0);--apb write data temp 51 52 begin 53 54 -- reset operation 55 if rst = '0' then 56 write_temp:= (others => '0'); 57 end if; 58 59 --write 60 if (apbi.psel(pindex) and apbi.penable and apbi.pwrite) = '1' then -- 61 case apbi.paddr(3 downto 2) is 62 when "00" => 63 write_temp:= apbi.pwdata; 64 when others => 65 null; 66 end case; 67 68 apb_write_reg<=write_temp(3 downto 0); 69 end if; 70 71 end process; 72 73 --clk process 74 regs : process(clk) 75 begin 76 if rising_edge(clk) then 77 ledindata_reg <= apb_write_reg;--一定要时钟才会跟新数据 78 end if; 79 end process; 80 81 --reconglise the vendor 82 apbo.pconfig <= pconfig; 83 84 --instant the entity 85 led1:led 86 port map( 87 indata=>ledindata_reg, 88 outdata=>led_out 89 ); 90 91 end architecture;
3 仿真测试
为了确保所定义的IP核能按照设计的工作,在将IP核嵌入到SOPC之前,需要对IP核的功能经行仿真。如图4所示(由于用的是Quartus II 9.0,并没有使用modelsim仿真)。
当APB的写信号有效后,APB数据线上的数据写入到ledindata_reg寄存器中,输出引脚输出对应共阳数码管的码值。例如APB总线上的数据是15时,输出引脚输出的就是10001110;当APB 总线上的数据是10000000时,IP输出引脚输出的就是10000000。仿真测试说明这个定义的IP核能正常工作。
图 4 IP核的仿真图
三、 将IP核嵌入到SOPC中
1 库文件准备
1、添加自定义IP库。为了方便IP核的管理和使用,需要添加自定义的IP库。在lib目录下新建文件夹,这里新建的是一个名为rcq的文件夹,作为自己的IP库。再在rcq里面新建一个名为seg的文件夹,作为七段数码管的IP库。将前面编写的led.vhd 和 apbseg.vhd放到seg中。
2、编写包文件。在seg文件夹中新建名为seg.vhd文件作为包文件。seg.vhd的内容如下:
1 library ieee; 2 use ieee.std_logic_1164.all; 3 library grlib; 4 use grlib.amba.all; 5 use grlib.stdlib.all; 6 use grlib.devices.all; 7 8 package seg is 9 10 type matrix_type is array (7 downto 0) of std_logic_vector (7 downto 0); 11 12 component apbseg is 13 generic ( 14 pindex : integer := 1 ;-- slave bus index 15 paddr : integer := 16#240#;-- slave address 16 pmask : integer := 16#fff#; --choose the minimun size 256byte 17 pirq : integer := 0 ---interrupt index 18 ); 19 port ( 20 rst : in std_ulogic; 21 clk : in std_ulogic; 22 apbi : in apb_slv_in_type; -- APB slave inputs 23 apbo : out apb_slv_out_type; -- APB slave outputs 24 led_out : out std_logic_vector(7 downto 0) 25 ); 26 27 end component; 28 29 end package;
3、另外,还需要修改leon3mp.qsf文件。在leon3mp.qsf中添加如下三行语句:
1 set_global_assignment -name VHDL_FILE http://www.cnblogs.com/lib/rcq/seg/seg.vhd -library rcq 2 set_global_assignment -name VHDL_FILE http://www.cnblogs.com/lib/rcq/seg/apbseg.vhd -library rcq 3 set_global_assignment -name VHDL_FILE http://www.cnblogs.com/lib/rcq/seg/led.vhd -library rcq
这样Quartus II 在综合的时候,就能找到前面定义的rcq 库中的所有设计文件。
2 添加IP
LEON 的配置和设计都是通过修改或者编写源代码来实现的。这里通过修改顶层文件的方式将IP添加到SOPC系统中。鉴于前面已经将一个简单的SOPC系统成功运行起来,这里在前面的基础上,只修改顶层leon3mp.vhd文件,将这个七段数码管的IP添加进去。
自定义的IP符合AMBA APB 总线协议,所以将其添加到APB总线上很容易。需要添加8个数码管,使用for generate语句生成。实现代码如下:
1 ----------------------------------------------------------------------- 2 --- HEX --------------------------------------- 3 ----------------------------------------------------------------------- 4 x:for i in 0 to 7 5 generate 6 hex:apbseg 7 generic map( 8 pindex => SEG_INDEX+i,-- slave bus index 9 paddr => SEG_INDEX+i-- slave address 10 ) 11 port map( 12 rst => rstn, 13 clk => clkm, 14 apbi => apbi, -- APB slave inputs 15 apbo => apbo(SEG_INDEX+i), -- APB slave outputs 16 led_out => hex_temp(i) 17 ); 18 end generate; 19 20 hexo_d_pad:outpadv 21 generic map (width =>7, tech => padtech) 22 port map (oHEX0_D, hex_temp(0)(6 downto 0)); 23 hexo_dp_pad:outpad 24 generic map (tech => padtech) 25 port map (oHEX0_DP, hex_temp(0)(7)); 26 27 hex1_d_pad:outpadv 28 generic map (width =>7, tech => padtech) 29 port map (oHEX1_D, hex_temp(1)(6 downto 0)); 30 hex1_dp_pad:outpad 31 generic map (tech => padtech) 32 port map (oHEX1_DP, hex_temp(1)(7)); 33 34 hex2_d_pad:outpadv 35 generic map (width =>7, tech => padtech) 36 port map (oHEX2_D, hex_temp(2)(6 downto 0)); 37 hex2_dp_pad:outpad 38 generic map (tech => padtech) 39 port map (oHEX2_DP, hex_temp(2)(7)); 40 41 hex3_d_pad:outpadv 42 generic map (width =>7, tech => padtech) 43 port map (oHEX3_D, hex_temp(3)(6 downto 0)); 44 hex3_dp_pad:outpad 45 generic map (tech => padtech) 46 port map (oHEX3_DP, hex_temp(3)(7)); 47 48 hex4_d_pad:outpadv 49 generic map (width =>7, tech => padtech) 50 port map (oHEX4_D, hex_temp(4)(6 downto 0)); 51 hex4_dp_pad:outpad 52 generic map (tech => padtech) 53 port map (oHEX4_DP, hex_temp(4)(7)); 54 55 hex5_d_pad:outpadv 56 generic map (width =>7, tech => padtech) 57 port map (oHEX5_D, hex_temp(5)(6 downto 0)); 58 hex5_dp_pad:outpad 59 generic map (tech => padtech) 60 port map (oHEX5_DP, hex_temp(5)(7)); 61 62 hex6_d_pad:outpadv 63 generic map (width =>7, tech => padtech) 64 port map (oHEX6_D, hex_temp(6)(6 downto 0)); 65 hex6_dp_pad:outpad 66 generic map (tech => padtech) 67 port map (oHEX6_DP, hex_temp(6)(7)); 68 69 hex7_d_pad:outpadv 70 generic map (width =>7, tech => padtech) 71 port map (oHEX7_D, hex_temp(7)(6 downto 0)); 72 hex7_dp_pad:outpad 73 generic map (tech => padtech) 74 port map (oHEX7_DP, hex_temp(7)(7));
注意:在AMBA设计的时候,默认APB 外设最多为16个,如果之前已经设计了很多APB 外设,在添加8个数码管的IP的时候,会出现错误。这种情况下,就需要修改AMBA的设计文件了。
3 测试硬件
启动Quartus II ,对修改后的工程进行编译综合、下载。
为了检测新生成的SOPC系统是否是符合要求的,可以使用GRTools中的Grmon工具测试硬件。
启动Grmon,在Action菜单下选择Connect to target,连接到目标。
图 5 Grmon连接到目标
如果连接成功,就能看到控制台输出如下信息:
1 GRMON LEON debug monitor v1.1.35b evaluation version 2 3 Copyright (C) 2004,2005 Gaisler Research - all rights reserved. 4 For latest updates, go to http://www.gaisler.com/ 5 Comments or bug-reports to support@gaisler.com 6 7 This evaluation version will expire on 27/5/2010 8 using Altera JTAG cable 9 Selected cable 1 - USB-Blaster [USB-0] 10 JTAG chain: 11 @1: EP2C70 (0x020B60DD) 12 13 GRLIB build version: 4104 14 15 initialising 16 detected frequency: 50 MHz 17 18 Component Vendor 19 LEON3 SPARC V8 Processor Gaisler Research 20 Unknown device Gaisler Research 21 AHB/APB Bridge Gaisler Research 22 LEON3 Debug Support Unit Gaisler Research 23 32-bit PC133 SDRAM Controller Gaisler Research 24 Generic APB UART Gaisler Research 25 Multi-processor Interrupt Ctrl Gaisler Research 26 Modular Timer Unit Gaisler Research 27 General purpose I/O port Gaisler Research 28 General purpose I/O port Gaisler Research 29 Unknown device Unknown vendor 30 Unknown device Unknown vendor 31 Unknown device Unknown vendor 32 Unknown device Unknown vendor 33 Unknown device Unknown vendor 34 Unknown device Unknown vendor 35 Unknown device Unknown vendor 36 Unknown device Unknown vendor 37 38 Use command 'info sys' to print a detailed report of attached cores 39 40 41 Grmon>
可以看到,系统中出现了8个 Unkown device,这个8个Unkown device就是自定义的8个数码管IP核,由于是用户自定义的,Vendor(提供商)和ID都是未知的,所以Grmon用 Unkown device 来表示。这表明我们自定义的数码管IP核已经添加到这个SOPC中。这时如果输入info sys 命令,获取系统更多的信息。
1 Grmon> info sys 2 00.01:003 Gaisler Research LEON3 SPARC V8 Processor (ver 0x0) 3 ahb master 0 4 01.01:01c Gaisler Research Unknown device (ver 0x1) 5 ahb master 1 6 01.01:006 Gaisler Research AHB/APB Bridge (ver 0x0) 7 ahb: 80000000 - 80100000 8 02.01:004 Gaisler Research LEON3 Debug Support Unit (ver 0x1) 9 ahb: 90000000 - a0000000 10 AHB trace 128 lines, stack pointer 0x43fffff0 11 CPU#0 win 8, hwbp 2, itrace 128, V8 mul/div, lddel 1 12 icache 2 * 8 kbyte, 32 byte/line lru 13 dcache 2 * 4 kbyte, 16 byte/line lru 14 03.01:009 Gaisler Research 32-bit PC133 SDRAM Controller (ver 0x1) 15 ahb: 40000000 - 50000000 16 ahb: fff00100 - fff00200 17 32-bit sdram: 1 * 64 Mbyte @ 0x40000000, col 9, cas 2, ref 7.8 us 18 01.01:00c Gaisler Research Generic APB UART (ver 0x1) 19 irq 2 20 apb: 80000100 - 80000200 21 baud rate 38343 22 02.01:00d Gaisler Research Multi-processor Interrupt Ctrl (ver 0x3) 23 apb: 80000200 - 80000300 24 03.01:011 Gaisler Research Modular Timer Unit (ver 0x0) 25 irq 8 26 apb: 80000300 - 80000400 27 8-bit scaler, 2 * 32-bit timers, divisor 50 28 05.01:01a Gaisler Research General purpose I/O port (ver 0x1) 29 apb: 80000500 - 80000600 30 06.01:01a Gaisler Research General purpose I/O port (ver 0x1) 31 apb: 80000600 - 80000700 32 08.07:001 Unknown vendor Unknown device (ver 0x0) 33 apb: 80000800 - 80000900 34 09.07:001 Unknown vendor Unknown device (ver 0x0) 35 apb: 80000900 - 80000a00 36 0a.07:001 Unknown vendor Unknown device (ver 0x0) 37 apb: 80000a00 - 80000b00 38 0b.07:001 Unknown vendor Unknown device (ver 0x0) 39 apb: 80000b00 - 80000c00 40 0c.07:001 Unknown vendor Unknown device (ver 0x0) 41 apb: 80000c00 - 80000d00 42 0d.07:001 Unknown vendor Unknown device (ver 0x0) 43 apb: 80000d00 - 80000e00 44 0e.07:001 Unknown vendor Unknown device (ver 0x0) 45 apb: 80000e00 - 80000f00 46 0f.07:001 Unknown vendor Unknown device (ver 0x0) 47 apb: 80000f00 - 80001000 48 49 Grmon>
这可更详细地看到,这8个IP的地址,分别是0x80000800、0x80000900、0x80000a00…0x80000f00。这些地址对应的寄存器就是前面在定义IP时候定义的输入数据寄存器。
四、 使用LEON IDE 测试IP核
知道了自定义IP核的地址和相关寄存器,就可以编程软件测试程序使用定义的IP核了。由于我们定义的数码管的IP核只有一个数据寄存器,只需要对它写值就可以进行测试了。
1 新建工程
同(原创)LEON3入门教程(三):基于LEON3的SOPC设计以及HELLOWORLD和流水灯介绍的一样,我们使用LEON IDE建立测试工程,为Seg_test。添加一个main.c文件,内容为:
1 /* 2 * main.c 3 * 4 * Created on: 2010-3-27 5 * Author: 超群天晴 6 */ 7 8 9 // LED 10 #define LEDG_BASE 0x80000600 11 12 // REG LEDG 13 #define LEDG_DATA_IN (*(unsigned int volatile *)(LEDG_BASE )) 14 #define LEDG_DATA_OUT (*(unsigned int volatile *)(LEDG_BASE + 4 )) 15 #define LEDG_DIR (*(unsigned int volatile *)(LEDG_BASE + 8 )) 16 #define LEDG_INT_MASK (*(unsigned int volatile *)(LEDG_BASE + 0xC )) 17 #define LEDG_INT_POL (*(unsigned int volatile *)(LEDG_BASE + 0x10 )) 18 #define LEDG_INT_EDGE (*(unsigned int volatile *)(LEDG_BASE + 0x14 )) 19 #define LEDG_BYPASS (*(unsigned int volatile *)(LEDG_BASE + 0x18 )) 20 21 22 // HEX 23 #define HEX_BASE 0x80000800 24 25 #define HEX0_DATA (*(unsigned int volatile *)(HEX_BASE + 0x000)) 26 #define HEX1_DATA (*(unsigned int volatile *)(HEX_BASE + 0x100)) 27 #define HEX2_DATA (*(unsigned int volatile *)(HEX_BASE + 0x200)) 28 #define HEX3_DATA (*(unsigned int volatile *)(HEX_BASE + 0x300)) 29 #define HEX4_DATA (*(unsigned int volatile *)(HEX_BASE + 0x400)) 30 #define HEX5_DATA (*(unsigned int volatile *)(HEX_BASE + 0x500)) 31 #define HEX6_DATA (*(unsigned int volatile *)(HEX_BASE + 0x600)) 32 #define HEX7_DATA (*(unsigned int volatile *)(HEX_BASE + 0x700)) 33 34 35 void delay1s(void); 36 int main(void) 37 { 38 unsigned char i=0; 39 40 unsigned char led1=0; 41 42 LEDG_DIR=0xffffffff;//output mode 43 44 led1=0xf0; 45 46 while(1) 47 { 48 for(i=0;i<16;i++) 49 { 50 LEDG_DATA_OUT=led1; 51 52 HEX0_DATA=i; 53 HEX1_DATA=i; 54 HEX2_DATA=i; 55 HEX3_DATA=i; 56 HEX4_DATA=i; 57 HEX5_DATA=i; 58 HEX6_DATA=i; 59 HEX7_DATA=i; 60 61 delay1s(); 62 63 led1=~led1;// 取反 64 } 65 } 66 67 return 1; 68 } 69 70 void delay1s(void) 71 { 72 int i,j; 73 for(i=0;i<1000;i++) 74 for(j=0;j<500;j++); 75 }
2 编译、运行测试程序
程序运行后,可以看到8个数码管交替显示0~F
图6 数码管IP测试结果
=======================
完整工程和代码可以从这里获取:LEON3-lab2.rar