프로세서, USER REGISTER ACCESS
일반적으로 FPGA 내부 프로세서를 사용할 경우 FPGA내에 설계하는 사용자 로직과 데이터를 주고받는 경우가 많습니다.
하지만 프로세서가 버스를 통해 user logic을 억세스하기 위해서는 버스 신호와 프로토콜을 이해해야 합니다. 정말 지루하고 따분하고 읽어도 읽어도 알아듣지 못할 말로 잔뜩 써 있는 것이 데이터 시트입니다.
XPS로 프로세서를 설계하면 user logic을 억세스하는 IP를 따로 제공하지 않기 때문에 설계자가 직접 만들어야 합니다.
뭐 프로토콜도 잘 알고 있고 프로세서의 동작상황도 잘 알고 있으면 못 만들 것도 없지만 생소한 프로그램에 낯선 프로세서와 처음 보는 프로토콜에 골탕 좀 먹으면 영 몹쓸 프로세서로 낙인 찍히기 쉽습니다.
그래서 이번 장에서는 user logic을 억세스하기 위한 제가 만든 IP를 사용하는 방법에 대해 살펴 보겠습니다.
n 내용 이해하기
일반적으로 임베디드 시스템을 설계하면 일단 프로세서를 기반으로 여러 주변장치들을 버스에 연결하고 동작을 상태를 점검하게 됩니다.
그러면 하나의 FPGA에는 하나의 임베디드 시스템을 구현하게 되는 거지요.
하지만 FPGA 내부에 임베디드 시스템을 하나만 구현하는 경우는 거의 없습니다. 다른 유저로직이 연결 됩니다.
이때 문제는 이 유저 로직과 임베디드 시스템간에 데이터를 교환해야 하는데 이 부분이 조금 애매 합니다.
일반적으로 FPGA 설계자라고 한다면 임베디드 시스템과 유저 로직간에 데이터를 주고 받을 때 필요한 신호 또는 프로토콜은 다음과 같이 생각하는 경우가 많습니다.
왜 자일링스에서 이런 기본적인 IP를 만들지 않는지는 모르겠어요.
뭐 여러가지 이유가 있겠지만 가장 큰 이유는 저 같은 사람 고생하라고 하는 것이 아닐까 합니다.
결국에 만들려고 하는 IP는 다음 그림과 같이 필요한 신호들을 유저 로직에서 볼 수 있도록 해주는 것 입니다.
데이터를 주고 받기 위한 신호들은 정리 했지만 이것을 어떻게 만드느냐?
그리고 실제 읽고 쓰는 타이밍은 어떻게 되는가에 대해서 차례대로 알아보겠습니다.
먼저 신호의 이름을 분석해 보겠습니다.
먼저 데이터 버스는 입력과 출력이 따로 정의된 것을 알 수 있습니다. 마스터가 데이터를 출력할 때는 data_to_user 포트를 통해 데이터를 출력하고 데이터 입력은 data_cs[3:0] 따라 data_from_user0 ~ data_from_user3 포트를 통해서 입력된다는 것으로 추정할 수 있습니다.
어드레스는 모두 10비트 이므로 1024개의 번지를 지정할 수 있습니다.
User_cs가 4비트이고 어드레스가 10비트이고 데이터가 32비트 이므로 4 * 1024 개의 32비트 워드를 디코딩할 수 있다는 얘기 입니다.
자 이런 것을 만들기 위해서 어떻게 해야 하는가?
막상 신호 자체는 간단해 보이지만 만들려고 하면 머리가 멍~~~ 해지는 것이 사실 입니다.
처음부터 뎀비면 마이 다칩니다.
먼저 자일링스에서 제공하는 기본적인 몇가지 기능을 알아 보도록 하겠습니다.
사실 이 글을 읽고 있는 분들은 마이크로블레이즈가 어떤 버스에서 어떤 프로토콜을 통해 데이터를 주고 받는지 알지 못할 겁니다.
그러니까 한 1000 여 페이지 정도 되는 데이터 쉬트를 읽어 보고 버스와 프로토콜을 이해해 본인 스스로 HDL 코딩하라고 하면 대부분 포기하게 되죠.
그래서 자일링스는 유저 로직을 억세스할 수 있는 기본 플랫폼을 제시해 줍니다.
자 이제 그 기본 플랫폼을 만들어 봅시다.
이 부분은 비디오 클립으로 보는 것이 좋을 것 같습니다.
설치된 폴더를 보니까 아래와 같습니다.
$ISE_INSTALL$\ISE_DS\edk_user_repository\MyProcessorIPLib\pcores
폴더 이름은 axi_user_logic_4096x32_4cs_10clk_v1_00_a 입니다.
axi_user_logic_4096x32_4cs_10clk_v1_00_a 폴더 밑에는 3개의 폴더가 기본적으로 있습니다.
Data
Dev
Hdl
여기서 저는 먼저 hdl 폴더에 먼저 관심을 가져 봅니다.
Hdl 폴더 밑, VHDL 폴더 밑에 보면 axi_user_logic_4096x32_4cs_10clk.vhd 파일과 user_logic.vhd 파일 2개가 있습니다.
먼저 user_logic.vhd를 보면 다음과 같습니다.
굳이 이 코드를 설명하려고 덤비는 이유는 다음과 같습니다. 먼저 버스 트랜잭션, 즉 버스를 통해 데이터를 주고 받을 때 언제 시작해서 언제 끝냐느냐를 알아야 하는데 … 이것 땜시 전체 소스를 가져다 붙였습니다.
1 ------------------------------------------------------------------------------
2 -- user_logic.vhd - entity/architecture pair
3 ------------------------------------------------------------------------------
4 --
5 -- ***************************************************************************
6 -- ** Copyright (c) 1995-2011 Xilinx, Inc. All rights reserved. **
7 -- ** **
8 -- ** Xilinx, Inc. **
9 -- ** XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" **
10 -- ** AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND **
11 -- ** SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, **
12 -- ** OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, **
13 -- ** APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION **
14 -- ** THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, **
15 -- ** AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE **
16 -- ** FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY **
17 -- ** WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE **
18 -- ** IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR **
19 -- ** REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF **
20 -- ** INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS **
21 -- ** FOR A PARTICULAR PURPOSE. **
22 -- ** **
23 -- ***************************************************************************
24 --
25 ------------------------------------------------------------------------------
26 -- Filename: user_logic.vhd
27 -- Version: 1.00.a
28 -- Description: User logic.
29 -- Date: Sat Dec 24 17:14:44 2011 (by Create and Import Peripheral Wizard)
30 -- VHDL Standard: VHDL'93
31 ------------------------------------------------------------------------------
32 -- Naming Conventions:
33 -- active low signals: "*_n"
34 -- clock signals: "clk", "clk_div#", "clk_#x"
35 -- reset signals: "rst", "rst_n"
36 -- generics: "C_*"
37 -- user defined types: "*_TYPE"
38 -- state machine next state: "*_ns"
39 -- state machine current state: "*_cs"
40 -- combinatorial signals: "*_com"
41 -- pipelined or register delay signals: "*_d#"
42 -- counter signals: "*cnt*"
43 -- clock enable signals: "*_ce"
44 -- internal version of output port: "*_i"
45 -- device pins: "*_pin"
46 -- ports: "- Names begin with Uppercase"
47 -- processes: "*_PROCESS"
48 -- component instantiations: "<ENTITY_>I_<#|FUNC>"
49 ------------------------------------------------------------------------------
50
51 -- DO NOT EDIT BELOW THIS LINE --------------------
52 library ieee;
53 use ieee.std_logic_1164.all;
54 use ieee.std_logic_arith.all;
55 use ieee.std_logic_unsigned.all;
56
57 library proc_common_v3_00_a;
58 use proc_common_v3_00_a.proc_common_pkg.all;
59
60 -- DO NOT EDIT ABOVE THIS LINE --------------------
61
62 --USER libraries added here
63
64 ------------------------------------------------------------------------------
65 -- Entity section
66 ------------------------------------------------------------------------------
67 -- Definition of Generics:
68 -- C_SLV_AWIDTH -- Slave interface address bus width
69 -- C_SLV_DWIDTH -- Slave interface data bus width
70 -- C_NUM_MEM -- Number of memory spaces
71 --
72 -- Definition of Ports:
73 -- Bus2IP_Clk -- Bus to IP clock
74 -- Bus2IP_Resetn -- Bus to IP reset
75 -- Bus2IP_Addr -- Bus to IP address bus
76 -- Bus2IP_CS -- Bus to IP chip select for user logic memory selection
77 -- Bus2IP_RNW -- Bus to IP read/not write
78 -- Bus2IP_Data -- Bus to IP data bus
79 -- Bus2IP_BE -- Bus to IP byte enables
80 -- Bus2IP_RdCE -- Bus to IP read chip enable
81 -- Bus2IP_WrCE -- Bus to IP write chip enable
82 -- Bus2IP_Burst -- Bus to IP burst-mode qualifier
83 -- Bus2IP_BurstLength -- Bus to IP burst length
84 -- Bus2IP_RdReq -- Bus to IP read request
85 -- Bus2IP_WrReq -- Bus to IP write request
86 -- IP2Bus_AddrAck -- IP to Bus address acknowledgement
87 -- IP2Bus_Data -- IP to Bus data bus
88 -- IP2Bus_RdAck -- IP to Bus read transfer acknowledgement
89 -- IP2Bus_WrAck -- IP to Bus write transfer acknowledgement
90 -- IP2Bus_Error -- IP to Bus error response
91 -- Type_of_xfer -- Transfer Type
92 ------------------------------------------------------------------------------
93
94 entity user_logic is
95 generic
96 (
97 -- ADD USER GENERICS BELOW THIS LINE ---------------
98 --USER generics added here
99 -- ADD USER GENERICS ABOVE THIS LINE ---------------
100
101 -- DO NOT EDIT BELOW THIS LINE ---------------------
102 -- Bus protocol parameters, do not add to or delete
103 C_SLV_AWIDTH : integer := 32;
104 C_SLV_DWIDTH : integer := 32;
105 C_NUM_MEM : integer := 4
106 -- DO NOT EDIT ABOVE THIS LINE ---------------------
107 );
108 port
109 (
110 -- ADD USER PORTS BELOW THIS LINE ------------------
111 --USER ports added here
112 -- ADD USER PORTS ABOVE THIS LINE ------------------
113
114 -- DO NOT EDIT BELOW THIS LINE ---------------------
115 -- Bus protocol ports, do not add to or delete
116 Bus2IP_Clk : in std_logic;
117 Bus2IP_Resetn : in std_logic;
118 Bus2IP_Addr : in std_logic_vector(C_SLV_AWIDTH-1 downto 0);
119 Bus2IP_CS : in std_logic_vector(C_NUM_MEM-1 downto 0);
120 Bus2IP_RNW : in std_logic;
121 Bus2IP_Data : in std_logic_vector(C_SLV_DWIDTH-1 downto 0);
122 Bus2IP_BE : in std_logic_vector(C_SLV_DWIDTH/8-1 downto 0);
123 Bus2IP_RdCE : in std_logic_vector(C_NUM_MEM-1 downto 0);
124 Bus2IP_WrCE : in std_logic_vector(C_NUM_MEM-1 downto 0);
125 Bus2IP_Burst : in std_logic;
126 Bus2IP_BurstLength : in std_logic_vector(7 downto 0);
127 Bus2IP_RdReq : in std_logic;
128 Bus2IP_WrReq : in std_logic;
129 IP2Bus_AddrAck : out std_logic;
130 IP2Bus_Data : out std_logic_vector(C_SLV_DWIDTH-1 downto 0);
131 IP2Bus_RdAck : out std_logic;
132 IP2Bus_WrAck : out std_logic;
133 IP2Bus_Error : out std_logic;
134 Type_of_xfer : out std_logic
135 -- DO NOT EDIT ABOVE THIS LINE ---------------------
136 );
137
138 attribute MAX_FANOUT : string;
139 attribute SIGIS : string;
140
141 attribute SIGIS of Bus2IP_Clk : signal is "CLK";
142 attribute SIGIS of Bus2IP_Resetn : signal is "RST";
143
144 end entity user_logic;
145
146 ------------------------------------------------------------------------------
147 -- Architecture section
148 ------------------------------------------------------------------------------
149
150 architecture IMP of user_logic is
151
152 --USER signal declarations added here, as needed for user logic
153
154 ------------------------------------------
155 -- Signals for user logic memory space example
156 ------------------------------------------
157 type BYTE_RAM_TYPE is array (0 to 255) of std_logic_vector(7 downto 0);
158 type DO_TYPE is array (0 to C_NUM_MEM-1) of std_logic_vector(C_SLV_DWIDTH-1 downto 0);
159 signal mem_data_out : DO_TYPE;
160 signal mem_address : std_logic_vector(7 downto 0);
161 signal mem_select : std_logic_vector(0 to 3);
162 signal mem_read_enable : std_logic;
163 signal mem_ip2bus_data : std_logic_vector(C_SLV_DWIDTH-1 downto 0);
164 signal mem_read_ack_dly1 : std_logic;
165 signal mem_read_ack_dly2 : std_logic;
166 signal mem_read_ack : std_logic;
167 signal mem_write_ack : std_logic;
168
169 begin
170
171 --USER logic implementation added here
172
173 ------------------------------------------
174 -- Example code to access user logic memory region
175 --
176 -- Note:
177 -- The example code presented here is to show you one way of using
178 -- the user logic memory space features. The Bus2IP_Addr, Bus2IP_CS,
179 -- and Bus2IP_RNW IPIC signals are dedicated to these user logic
180 -- memory spaces. Each user logic memory space has its own address
181 -- range and is allocated one bit on the Bus2IP_CS signal to indicated
182 -- selection of that memory space. Typically these user logic memory
183 -- spaces are used to implement memory controller type cores, but it
184 -- can also be used in cores that need to access additional address space
185 -- (non C_BASEADDR based), s.t. bridges. This code snippet infers
186 -- 4 256x32-bit (byte accessible) single-port Block RAM by XST.
187 ------------------------------------------
188 mem_select <= Bus2IP_CS;
189 mem_read_enable <= ( Bus2IP_RdCE(0) or Bus2IP_RdCE(1) or Bus2IP_RdCE(2) or Bus2IP_RdCE(3) );
190 mem_read_ack <= mem_read_ack_dly1 and (not mem_read_ack_dly2);
191 mem_write_ack <= ( Bus2IP_WrCE(0) or Bus2IP_WrCE(1) or Bus2IP_WrCE(2) or Bus2IP_WrCE(3) );
192 mem_address <= Bus2IP_Addr(9 downto 2);
193
194 -- this process generates the read acknowledge 1 clock after read enable
195 -- is presented to the BRAM block. The BRAM block has a 1 clock delay
196 -- from read enable to data out.
197 BRAM_RD_ACK_PROC : process( Bus2IP_Clk ) is
198 begin
199
200 if ( Bus2IP_Clk'event and Bus2IP_Clk = '1' ) then
201 if ( Bus2IP_Resetn = '0' ) then
202 mem_read_ack_dly1 <= '0';
203 mem_read_ack_dly2 <= '0';
204 else
205 mem_read_ack_dly1 <= mem_read_enable;
206 mem_read_ack_dly2 <= mem_read_ack_dly1;
207 end if;
208 end if;
209
210 end process BRAM_RD_ACK_PROC;
211
212 -- implement Block RAM(s)
213 BRAM_GEN : for i in 0 to C_NUM_MEM-1 generate
214 constant NUM_BYTE_LANES : integer := (C_SLV_DWIDTH+7)/8;
215 begin
216
217 BYTE_BRAM_GEN : for byte_index in 0 to NUM_BYTE_LANES-1 generate
218 signal ram : BYTE_RAM_TYPE;
219 signal write_enable : std_logic;
220 signal data_in : std_logic_vector(7 downto 0);
221 signal data_out : std_logic_vector(7 downto 0);
222 signal read_address : std_logic_vector(7 downto 0);
223 begin
224
225 write_enable <= Bus2IP_WrCE(i) and Bus2IP_BE(byte_index);
226
227 data_in <= Bus2IP_Data(byte_index*8+7 downto byte_index*8);
228 BYTE_RAM_PROC : process( Bus2IP_Clk ) is
229 begin
230
231 if ( Bus2IP_Clk'event and Bus2IP_Clk = '1' ) then
232 if ( write_enable = '1' ) then
233 ram(CONV_INTEGER(mem_address)) <= data_in;
234 end if;
235 read_address <= mem_address;
236 end if;
237
238 end process BYTE_RAM_PROC;
239
240 data_out <= ram(CONV_INTEGER(read_address));
241
242 mem_data_out(i)(byte_index*8+7 downto byte_index*8) <= data_out;
243
244 end generate BYTE_BRAM_GEN;
245
246 end generate BRAM_GEN;
247
248 -- implement Block RAM read mux
249 MEM_IP2BUS_DATA_PROC : process( mem_data_out, mem_select ) is
250 begin
251
252 case mem_select is
253 when "0001" => mem_ip2bus_data <= mem_data_out(0);
254 when "0010" => mem_ip2bus_data <= mem_data_out(1);
255 when "0100" => mem_ip2bus_data <= mem_data_out(2);
256 when "1000" => mem_ip2bus_data <= mem_data_out(3);
257 when others => mem_ip2bus_data <= (others => '0');
258 end case;
259
260 end process MEM_IP2BUS_DATA_PROC;
261
262 ------------------------------------------
263 -- Example code to drive IP to Bus signals
264 ------------------------------------------
265 IP2Bus_Data <= mem_ip2bus_data when mem_read_ack = '1' else
266 (others => '0');
267
268 IP2Bus_AddrAck <= mem_write_ack or (mem_read_enable and mem_read_ack);
269 IP2Bus_WrAck <= mem_write_ack;
270 IP2Bus_RdAck <= mem_read_ack;
271 IP2Bus_Error <= '0';
272
273 end IMP;
전체 entity의 이름은 user logic이고요108라인부터 보면 포트를 정의했는데 111라인을 보면 “user port는 여기에다 정의해라” 라는 친절한 주석문이 있습니다.
116라인부터 134라인까지는 아마도 프로세서로에서 사용하는 버스의 신호라고 예상할 수 있습니다. 슬슬 읽어보면 대충 감을 때릴 수 있는데 조금 햇갈리는 것은 burst라든지 burst length 라는 것이 여기는 설명하지 않을 테니까 넘어가고요.
AddrAck, RaAck, WrAck같은 신호를 보니 음… 버스가 정해진 클럭에 트랜잭션이 끝나는 것이 아니고 슬레이브 쪽에서 응답을 해줘야 하나보다… 이렇게 추측할 수 있습니다.
신호이름들의 특성을 보면 Bus è IP, IP è Bus로 왔다 갔다 하는 방향이 정확힐 설정되어 있습니다. 다른 말로 입출력 포트가 아니고 입력이나 출력으로 단방향으로 정해졌다는 얘기 입니다.
당연한 얘기겠죠. FPGA 내부에서는 BUFT 가 없으니까요.
108 port
109 (
110 -- ADD USER PORTS BELOW THIS LINE ------------------
111 --USER ports added here
112 -- ADD USER PORTS ABOVE THIS LINE ------------------
113
114 -- DO NOT EDIT BELOW THIS LINE ---------------------
115 -- Bus protocol ports, do not add to or delete
116 Bus2IP_Clk : in std_logic;
117 Bus2IP_Resetn : in std_logic;
118 Bus2IP_Addr : in std_logic_vector(C_SLV_AWIDTH-1 downto 0);
119 Bus2IP_CS : in std_logic_vector(C_NUM_MEM-1 downto 0);
120 Bus2IP_RNW : in std_logic;
121 Bus2IP_Data : in std_logic_vector(C_SLV_DWIDTH-1 downto 0);
122 Bus2IP_BE : in std_logic_vector(C_SLV_DWIDTH/8-1 downto 0);
123 Bus2IP_RdCE : in std_logic_vector(C_NUM_MEM-1 downto 0);
124 Bus2IP_WrCE : in std_logic_vector(C_NUM_MEM-1 downto 0);
125 Bus2IP_Burst : in std_logic;
126 Bus2IP_BurstLength : in std_logic_vector(7 downto 0);
127 Bus2IP_RdReq : in std_logic;
128 Bus2IP_WrReq : in std_logic;
129 IP2Bus_AddrAck : out std_logic;
130 IP2Bus_Data : out std_logic_vector(C_SLV_DWIDTH-1 downto 0);
131 IP2Bus_RdAck : out std_logic;
132 IP2Bus_WrAck : out std_logic;
133 IP2Bus_Error : out std_logic;
134 Type_of_xfer : out std_logic
135 -- DO NOT EDIT ABOVE THIS LINE ---------------------
136 );
자 이제 entity 내부는 어떻게 구현되어 있는지 살펴보죠.
다음 코드는 메모리를 구현하는 VHDL 코드 입니다.
먼저 157, 158 라인에서 메모리 타입을 선언하고 8비트짜리 256개를 모두 4개 선언했네요. 그리고 나서 212라인부터 246 라인까지 BRAM을 인~퍼~런~싱~ 했습니다.
인퍼런싱이라는 것은 코드를 보면 내가 BRAM을 쓰지는 않았지만 합성툴이 가만보고 “아~~ 이놈의 레지스터 집합의 동작 특성을 보니 메모리구만… 그러면 BRAM으로 합성해야지”라고 자동으로 판단해서 BRAM을 사용하도록 해주는 코딩 스타일 입니다.
조금 더 자세히 애기하자면 합성툴은 레지스터의 크기와 동작특성을 보고 그냥 레지스터로 합성할지, Distributed Ram으로 합성할지 BRAM으로 합성할지 자동적으로 판단 합니다.
여기서는 입력과 출력이 모두 클럭에 동기가 되고 레지스터의 크기가 비교적 크다고 판단했기 때문에 합성툴은 자동으로 BRAM으로 합니다.
즉 프로젝트가 바꿔도 디바이스 벤더가 바꿔도 (제발 그러지는 말아 주세요 흑흑) 같은 코드를 쓸 수 있다는 겁니다.
실제로 프로젝트할 때마다 바꿔보세요…
코드자체에서 어려운 점은 없어요.
어려우면 VHDL 공부부터 다시 하세요.
157 type BYTE_RAM_TYPE is array (0 to 255) of std_logic_vector(7 downto 0);
158 type DO_TYPE is array (0 to C_NUM_MEM-1) of std_logic_vector(C_SLV_DWIDTH-1 downto 0);
212 -- implement Block RAM(s)
213 BRAM_GEN : for i in 0 to C_NUM_MEM-1 generate
214 constant NUM_BYTE_LANES : integer := (C_SLV_DWIDTH+7)/8;
215 begin
216
217 BYTE_BRAM_GEN : for byte_index in 0 to NUM_BYTE_LANES-1 generate
218 signal ram : BYTE_RAM_TYPE;
219 signal write_enable : std_logic;
220 signal data_in : std_logic_vector(7 downto 0);
221 signal data_out : std_logic_vector(7 downto 0);
222 signal read_address : std_logic_vector(7 downto 0);
223 begin
224
225 write_enable <= Bus2IP_WrCE(i) and Bus2IP_BE(byte_index);
226
227 data_in <= Bus2IP_Data(byte_index*8+7 downto byte_index*8);
228 BYTE_RAM_PROC : process( Bus2IP_Clk ) is
229 begin
230
231 if ( Bus2IP_Clk'event and Bus2IP_Clk = '1' ) then
232 if ( write_enable = '1' ) then
233 ram(CONV_INTEGER(mem_address)) <= data_in;
234 end if;
235 read_address <= mem_address;
236 end if;
237
238 end process BYTE_RAM_PROC;
239
240 data_out <= ram(CONV_INTEGER(read_address));
241
242 mem_data_out(i)(byte_index*8+7 downto byte_index*8) <= data_out;
243
244 end generate BYTE_BRAM_GEN;
245
246 end generate BRAM_GEN
자 다음은 2가지를 한꺼번에 보겠습니다. 먼저 249라인을 보면 MUX가 하나 구현되어 있습니다.
CASE ~ WHEN 보면 이런 회로도가 당연히 떠 올라야 해요
안떠오르면
VHDL 공부부터 다시 하세요.
간단하네요 mem_select에 따라서 mem_ip2bus_data에 연결되는 포트가 4개중 하나로 결정되네요.
두번째가 중요 합니다.
이렇게 mux에서 선택된 최정 출력은 다시 265라인에서 IP2Bus_data로 연결되는데 그 시점이 언제냐 하면 mem_read_ack가 ‘1일 때 입니다. 그렇지 않으면 ‘0’ 입니다.
그러면 언제 mem_read_ack를 ‘1’ 만드느냐는 조금 있다가 설명하고 나머지 268, 269, 270 라인의 IP2Bus_*Ack 신호 삼총사는 일단 슬레이브에서 만드는 것을 확인할 수 있습니다.
271라인의 IP2Bus_Error은 그냥 ‘0’으로 묶어 둡니다.
248 -- implement Block RAM read mux
249 MEM_IP2BUS_DATA_PROC : process( mem_data_out, mem_select ) is
250 begin
251
252 case mem_select is
253 when "0001" => mem_ip2bus_data <= mem_data_out(0);
254 when "0010" => mem_ip2bus_data <= mem_data_out(1);
255 when "0100" => mem_ip2bus_data <= mem_data_out(2);
256 when "1000" => mem_ip2bus_data <= mem_data_out(3);
257 when others => mem_ip2bus_data <= (others => '0');
258 end case;
259
260 end process MEM_IP2BUS_DATA_PROC;
261
262 ------------------------------------------
263 -- Example code to drive IP to Bus signals
264 ------------------------------------------
265 IP2Bus_Data <= mem_ip2bus_data when mem_read_ack = '1' else
266 (others => '0');
267
268 IP2Bus_AddrAck <= mem_write_ack or (mem_read_enable and mem_read_ack);
269 IP2Bus_WrAck <= mem_write_ack;
270 IP2Bus_RdAck <= mem_read_ack;
271 IP2Bus_Error <= '0';
272
자 이제 이 ACK 삼총사를 설명하려고 합니다.
먼저 188라인에서 두개의 신호는 서로 연결되었고요 189라인을 보면 4개의 RdCE가 OR연산을 하네요. 4개중 어느 하나라도 ‘1이면 mem_read_enable은 ‘1’이 되죠.
'edk > user logic interface' 카테고리의 다른 글
axi_user_bram_if_ctrl (0) | 2012.02.08 |
---|---|
user logic interface axi 16 bits (0) | 2012.01.27 |
마이크로블레이즈가 register를 읽고 쓰기 위해 필요한 IP 및 설명 (0) | 2011.11.19 |