1 |
25 |
skordal |
-- The Potato Processor - A simple processor for FPGAs
|
2 |
|
|
-- (c) Kristian Klomsten Skordal 2014 - 2015 <kristian.skordal@wafflemail.net>
|
3 |
|
|
-- Report bugs and issues on <http://opencores.org/project,potato,bugtracker>
|
4 |
|
|
|
5 |
|
|
library ieee;
|
6 |
|
|
use ieee.std_logic_1164.all;
|
7 |
|
|
use ieee.numeric_std.all;
|
8 |
|
|
|
9 |
|
|
use work.pp_types.all;
|
10 |
|
|
use work.pp_utilities.all;
|
11 |
|
|
|
12 |
44 |
skordal |
--! @brief Simple read-only direct-mapped instruction cache.
|
13 |
|
|
entity pp_icache is
|
14 |
25 |
skordal |
generic(
|
15 |
44 |
skordal |
LINE_SIZE : natural := 4; --! Number of words per cache line
|
16 |
|
|
NUM_LINES : natural := 128 --! Number of lines in the cache
|
17 |
25 |
skordal |
);
|
18 |
|
|
port(
|
19 |
|
|
clk : in std_logic;
|
20 |
|
|
reset : in std_logic;
|
21 |
|
|
|
22 |
|
|
-- Control interface:
|
23 |
|
|
cache_enable : in std_logic;
|
24 |
|
|
cache_flush : in std_logic;
|
25 |
|
|
cached_areas : in std_logic_vector(31 downto 0);
|
26 |
|
|
|
27 |
|
|
-- Memory interface:
|
28 |
|
|
mem_address_in : in std_logic_vector(31 downto 0);
|
29 |
|
|
mem_data_in : in std_logic_vector(31 downto 0);
|
30 |
|
|
mem_data_out : out std_logic_vector(31 downto 0);
|
31 |
|
|
mem_data_size : in std_logic_vector( 1 downto 0);
|
32 |
|
|
mem_read_req : in std_logic;
|
33 |
|
|
mem_read_ack : out std_logic;
|
34 |
|
|
mem_write_req : in std_logic;
|
35 |
|
|
mem_write_ack : out std_logic;
|
36 |
|
|
|
37 |
|
|
-- Wishbone interface:
|
38 |
|
|
wb_inputs : in wishbone_master_inputs;
|
39 |
|
|
wb_outputs : out wishbone_master_outputs
|
40 |
|
|
);
|
41 |
44 |
skordal |
end entity pp_icache;
|
42 |
25 |
skordal |
|
43 |
44 |
skordal |
architecture behaviour of pp_icache is
|
44 |
25 |
skordal |
|
45 |
44 |
skordal |
-- Counter types:
|
46 |
|
|
subtype line_counter_type is natural range 0 to NUM_LINES;
|
47 |
|
|
subtype word_counter_type is natural range 0 to LINE_SIZE;
|
48 |
25 |
skordal |
|
49 |
44 |
skordal |
-- Cache line types:
|
50 |
|
|
subtype cache_line_type is std_logic_vector((LINE_SIZE * 32) - 1 downto 0);
|
51 |
|
|
type cache_line_word_array is array(0 to LINE_SIZE - 1) of std_logic_vector(31 downto 0);
|
52 |
|
|
type cache_line_array is array(0 to NUM_LINES - 1) of cache_line_type;
|
53 |
|
|
|
54 |
|
|
-- Cache tag type:
|
55 |
|
|
subtype cache_tag_type is std_logic_vector(31 - log2(LINE_SIZE * 4) - log2(NUM_LINES) downto 0);
|
56 |
|
|
type cache_tag_array is array(0 to NUM_LINES - 1) of cache_tag_type;
|
57 |
|
|
|
58 |
|
|
-- Cache memories:
|
59 |
|
|
signal cache_memory : cache_line_array;
|
60 |
|
|
signal tag_memory : cache_tag_array;
|
61 |
|
|
signal valid : std_logic_vector(NUM_LINES - 1 downto 0) := (others => '0');
|
62 |
|
|
|
63 |
|
|
attribute ram_style : string;
|
64 |
|
|
attribute ram_style of cache_memory: signal is "block";
|
65 |
|
|
--attribute ram_style of tag_memory: signal is "block";
|
66 |
|
|
|
67 |
25 |
skordal |
-- Cache controller signals:
|
68 |
44 |
skordal |
type state_type is (IDLE, CACHE_READ_STALL, SINGLE_READ, SINGLE_WRITE,
|
69 |
|
|
LOAD_CACHELINE_START, LOAD_CACHELINE_WAIT_ACK, LOAD_CACHELINE_FINISH);
|
70 |
25 |
skordal |
signal state : state_type := IDLE;
|
71 |
|
|
|
72 |
44 |
skordal |
-- Is the current input address in the cache?
|
73 |
|
|
signal input_address_cached : boolean;
|
74 |
|
|
|
75 |
|
|
-- Input address components:
|
76 |
|
|
signal input_address_line : std_logic_vector(log2(NUM_LINES) - 1 downto 0);
|
77 |
|
|
signal input_address_word : std_logic_vector(log2(LINE_SIZE) - 1 downto 0);
|
78 |
|
|
signal input_address_tag : std_logic_vector(31 - log2(LINE_SIZE * 4) - log2(NUM_LINES) downto 0);
|
79 |
|
|
|
80 |
|
|
-- Cacheline matching the current input address:
|
81 |
|
|
signal current_cache_line, cache_lookup : cache_line_type;
|
82 |
|
|
signal current_cache_line_words : cache_line_word_array;
|
83 |
|
|
signal current_tag : cache_tag_type;
|
84 |
|
|
|
85 |
|
|
-- Base address to store a cacheline to:
|
86 |
|
|
signal cl_store_address : std_logic_vector(31 downto log2(LINE_SIZE * 4));
|
87 |
|
|
-- Base address to load a cacheline from:
|
88 |
|
|
signal cl_load_address : std_logic_vector(31 downto log2(LINE_SIZE * 4));
|
89 |
|
|
-- Cache line to load:
|
90 |
|
|
signal cl_current_line : line_counter_type;
|
91 |
|
|
-- Current word being loaded/stored:
|
92 |
|
|
signal cl_current_word : word_counter_type;
|
93 |
|
|
|
94 |
|
|
-- Buffer for holding a cache line while loading:
|
95 |
|
|
signal load_buffer : cache_line_type;
|
96 |
|
|
signal load_buffer_tag : cache_tag_type;
|
97 |
|
|
|
98 |
|
|
-- Causes a cache line to be stored in the cache memory:
|
99 |
|
|
signal store_cache_line : std_logic;
|
100 |
|
|
|
101 |
|
|
-- Set when the current input address matches a cache line:
|
102 |
|
|
signal cache_hit : std_logic;
|
103 |
|
|
|
104 |
|
|
-- For regular reads:
|
105 |
|
|
signal read_ack : std_logic;
|
106 |
|
|
signal read_data_out : std_logic_vector(31 downto 0);
|
107 |
|
|
|
108 |
|
|
-- For regular writes:
|
109 |
|
|
signal write_ack : std_logic;
|
110 |
|
|
|
111 |
25 |
skordal |
-- Gets the amount to shift output data to the processor with for requests of size != 32 bits:
|
112 |
|
|
function get_data_shift(size : in std_logic_vector(1 downto 0); address : in std_logic_vector)
|
113 |
|
|
return natural is
|
114 |
|
|
begin
|
115 |
|
|
case size is
|
116 |
|
|
when b"01" =>
|
117 |
|
|
case address(1 downto 0) is
|
118 |
|
|
when b"00" =>
|
119 |
|
|
return 0;
|
120 |
|
|
when b"01" =>
|
121 |
|
|
return 8;
|
122 |
|
|
when b"10" =>
|
123 |
|
|
return 16;
|
124 |
|
|
when b"11" =>
|
125 |
|
|
return 24;
|
126 |
|
|
when others =>
|
127 |
|
|
return 0;
|
128 |
|
|
end case;
|
129 |
|
|
when b"10" =>
|
130 |
|
|
if address(1) = '0' then
|
131 |
|
|
return 0;
|
132 |
|
|
else
|
133 |
|
|
return 16;
|
134 |
|
|
end if;
|
135 |
|
|
when others =>
|
136 |
|
|
return 0;
|
137 |
|
|
end case;
|
138 |
|
|
end function get_data_shift;
|
139 |
|
|
|
140 |
|
|
begin
|
141 |
|
|
|
142 |
44 |
skordal |
--assert is_pow2(LINE_SIZE) report "Cache line size must be a power of 2!" severity FAILURE;
|
143 |
|
|
--assert is_pow2(NUM_LINES) report "Number of cache lines must be a power of 2!" severity FAILURE;
|
144 |
25 |
skordal |
|
145 |
|
|
-- Check if the current input address should be/is in the cache:
|
146 |
44 |
skordal |
input_address_cached <= cached_areas(to_integer(unsigned(mem_address_in(31 downto 27)))) = '1';
|
147 |
25 |
skordal |
|
148 |
44 |
skordal |
mem_data_out <= current_cache_line_words(to_integer(unsigned(input_address_word))) when
|
149 |
|
|
input_address_cached and cache_enable = '1' and cache_flush = '0'
|
150 |
|
|
else read_data_out;
|
151 |
|
|
mem_read_ack <= (cache_hit and mem_read_req)
|
152 |
|
|
when state = IDLE and input_address_cached and cache_enable = '1' and cache_flush = '0'
|
153 |
|
|
else read_ack;
|
154 |
|
|
write_ack <= wb_inputs.ack when state = SINGLE_WRITE else '0';
|
155 |
|
|
mem_write_ack <= write_ack;
|
156 |
25 |
skordal |
|
157 |
44 |
skordal |
input_address_line <= mem_address_in(log2(LINE_SIZE * 4) + log2(NUM_LINES) - 1 downto log2(LINE_SIZE * 4));
|
158 |
|
|
input_address_tag <= mem_address_in(31 downto log2(LINE_SIZE * 4) + log2(NUM_LINES));
|
159 |
|
|
|
160 |
|
|
find_word: process(clk)
|
161 |
|
|
begin
|
162 |
|
|
if rising_edge(clk) then
|
163 |
|
|
input_address_word <= mem_address_in(log2(LINE_SIZE * 4) - 1 downto 2);
|
164 |
|
|
end if;
|
165 |
|
|
end process find_word;
|
166 |
|
|
|
167 |
|
|
cacheline_lookup: process(clk)
|
168 |
|
|
begin
|
169 |
|
|
if rising_edge(clk) then
|
170 |
|
|
if store_cache_line = '1' then
|
171 |
|
|
cache_memory(cl_current_line) <= load_buffer;
|
172 |
|
|
end if;
|
173 |
|
|
|
174 |
|
|
current_cache_line <= cache_memory(to_integer(unsigned(input_address_line)));
|
175 |
|
|
end if;
|
176 |
|
|
end process cacheline_lookup;
|
177 |
|
|
|
178 |
|
|
decompose_cache_line: for i in 0 to LINE_SIZE - 1 generate
|
179 |
|
|
current_cache_line_words(i) <= current_cache_line(32 * i + 31 downto 32 * i);
|
180 |
|
|
end generate decompose_cache_line;
|
181 |
|
|
|
182 |
|
|
tag_lookup: process(clk)
|
183 |
|
|
begin
|
184 |
|
|
if rising_edge(clk) then
|
185 |
|
|
if reset = '1' then
|
186 |
|
|
cache_hit <= '0';
|
187 |
|
|
else
|
188 |
|
|
if store_cache_line = '1' then
|
189 |
|
|
tag_memory(cl_current_line) <= load_buffer_tag;
|
190 |
|
|
end if;
|
191 |
|
|
|
192 |
|
|
current_tag <= tag_memory(to_integer(unsigned(input_address_line)));
|
193 |
|
|
cache_hit <= valid(to_integer(unsigned(input_address_line))) and to_std_logic(tag_memory(to_integer(unsigned(input_address_line))) = input_address_tag);
|
194 |
|
|
end if;
|
195 |
|
|
end if;
|
196 |
|
|
end process tag_lookup;
|
197 |
|
|
|
198 |
25 |
skordal |
controller: process(clk)
|
199 |
44 |
skordal |
variable current_word : std_logic_vector(31 downto 0);
|
200 |
25 |
skordal |
begin
|
201 |
|
|
if rising_edge(clk) then
|
202 |
|
|
if reset = '1' then
|
203 |
|
|
state <= IDLE;
|
204 |
|
|
wb_outputs.cyc <= '0';
|
205 |
|
|
wb_outputs.stb <= '0';
|
206 |
44 |
skordal |
store_cache_line <= '0';
|
207 |
|
|
read_ack <= '0';
|
208 |
|
|
valid <= (others => '0');
|
209 |
|
|
read_data_out <= (others => '0');
|
210 |
25 |
skordal |
else
|
211 |
|
|
case state is
|
212 |
|
|
when IDLE =>
|
213 |
44 |
skordal |
read_ack <= '0';
|
214 |
|
|
if cache_flush = '1' then
|
215 |
|
|
valid <= (others => '0');
|
216 |
|
|
elsif input_address_cached and cache_enable = '1' then
|
217 |
|
|
if (mem_read_req = '1' or mem_write_req = '1') and cache_hit = '0' then
|
218 |
|
|
wb_outputs.adr <= mem_address_in(31 downto log2(LINE_SIZE * 4)) & (log2(LINE_SIZE * 4) - 1 downto 0 => '0');
|
219 |
|
|
wb_outputs.cyc <= '1';
|
220 |
|
|
wb_outputs.we <= '0';
|
221 |
|
|
wb_outputs.sel <= (others => '1');
|
222 |
|
|
load_buffer_tag <= input_address_tag;
|
223 |
|
|
cl_load_address <= mem_address_in(31 downto log2(LINE_SIZE * 4));
|
224 |
|
|
cl_store_address <= input_address_tag & input_address_line;
|
225 |
|
|
cl_current_line <= to_integer(unsigned(input_address_line));
|
226 |
|
|
cl_current_word <= 0;
|
227 |
|
|
state <= LOAD_CACHELINE_START;
|
228 |
|
|
end if;
|
229 |
|
|
else
|
230 |
|
|
if mem_read_req = '1' and read_ack = '0' then -- Do an uncached read
|
231 |
25 |
skordal |
wb_outputs.adr <= mem_address_in;
|
232 |
44 |
skordal |
wb_outputs.sel <= wb_get_data_sel(mem_data_size, mem_address_in);
|
233 |
25 |
skordal |
wb_outputs.cyc <= '1';
|
234 |
|
|
wb_outputs.stb <= '1';
|
235 |
|
|
wb_outputs.we <= '0';
|
236 |
|
|
state <= SINGLE_READ;
|
237 |
|
|
elsif mem_write_req = '1' then -- Do an uncached write
|
238 |
|
|
wb_outputs.adr <= mem_address_in;
|
239 |
|
|
wb_outputs.dat <= std_logic_vector(shift_left(unsigned(mem_data_in),
|
240 |
|
|
get_data_shift(mem_data_size, mem_address_in)));
|
241 |
44 |
skordal |
wb_outputs.sel <= wb_get_data_sel(mem_data_size, mem_address_in);
|
242 |
25 |
skordal |
wb_outputs.cyc <= '1';
|
243 |
|
|
wb_outputs.stb <= '1';
|
244 |
|
|
wb_outputs.we <= '1';
|
245 |
|
|
state <= SINGLE_WRITE;
|
246 |
|
|
end if;
|
247 |
44 |
skordal |
end if;
|
248 |
|
|
when CACHE_READ_STALL =>
|
249 |
|
|
state <= IDLE;
|
250 |
25 |
skordal |
when SINGLE_READ =>
|
251 |
|
|
if wb_inputs.ack = '1' then
|
252 |
44 |
skordal |
read_data_out <= std_logic_vector(shift_right(unsigned(wb_inputs.dat),
|
253 |
25 |
skordal |
get_data_shift(mem_data_size, mem_address_in)));
|
254 |
|
|
wb_outputs.cyc <= '0';
|
255 |
|
|
wb_outputs.stb <= '0';
|
256 |
44 |
skordal |
read_ack <= '1';
|
257 |
25 |
skordal |
state <= IDLE;
|
258 |
|
|
end if;
|
259 |
|
|
when SINGLE_WRITE =>
|
260 |
|
|
if wb_inputs.ack = '1' then
|
261 |
|
|
wb_outputs.cyc <= '0';
|
262 |
|
|
wb_outputs.stb <= '0';
|
263 |
|
|
wb_outputs.we <= '0';
|
264 |
|
|
state <= IDLE;
|
265 |
|
|
end if;
|
266 |
44 |
skordal |
when LOAD_CACHELINE_START =>
|
267 |
|
|
wb_outputs.stb <= '1';
|
268 |
|
|
wb_outputs.we <= '0';
|
269 |
|
|
wb_outputs.adr <= cl_load_address & std_logic_vector(to_unsigned(cl_current_word, log2(LINE_SIZE))) & b"00";
|
270 |
|
|
state <= LOAD_CACHELINE_WAIT_ACK;
|
271 |
|
|
when LOAD_CACHELINE_WAIT_ACK =>
|
272 |
|
|
if wb_inputs.ack = '1' then
|
273 |
|
|
wb_outputs.stb <= '0';
|
274 |
|
|
load_buffer(cl_current_word * 32 + 31 downto cl_current_word * 32) <= wb_inputs.dat;
|
275 |
|
|
if natural(cl_current_word) = LINE_SIZE - 1 then
|
276 |
|
|
wb_outputs.cyc <= '0';
|
277 |
|
|
store_cache_line <= '1';
|
278 |
|
|
state <= LOAD_CACHELINE_FINISH;
|
279 |
|
|
else
|
280 |
|
|
cl_current_word <= cl_current_word + 1;
|
281 |
|
|
state <= LOAD_CACHELINE_START;
|
282 |
|
|
end if;
|
283 |
|
|
end if;
|
284 |
|
|
when LOAD_CACHELINE_FINISH =>
|
285 |
|
|
store_cache_line <= '0';
|
286 |
|
|
valid(cl_current_line) <= '1';
|
287 |
|
|
state <= CACHE_READ_STALL;
|
288 |
25 |
skordal |
end case;
|
289 |
|
|
end if;
|
290 |
|
|
end if;
|
291 |
|
|
end process controller;
|
292 |
|
|
|
293 |
|
|
end architecture behaviour;
|