摘要:這一小節將介紹下如何設計用戶自定義的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