1 |
8 |
mcleod_ide |
`timescale 1ns / 1ps
|
2 |
|
|
//////////////////////////////////////////////////////////////////////////////////
|
3 |
|
|
// Company: Dept. Architecture and Computing Technology. University of Seville
|
4 |
|
|
// Engineer: Miguel Angel Rodriguez Jodar. rodriguj@atc.us.es
|
5 |
|
|
//
|
6 |
|
|
// Create Date: 19:13:39 4-Apr-2012
|
7 |
|
|
// Design Name: ZX Spectrum
|
8 |
|
|
// Module Name: ram32k
|
9 |
|
|
// Project Name:
|
10 |
|
|
// Target Devices:
|
11 |
|
|
// Tool versions:
|
12 |
|
|
// Description:
|
13 |
|
|
//
|
14 |
|
|
// Dependencies:
|
15 |
|
|
//
|
16 |
|
|
// Revision:
|
17 |
|
|
// Revision 1.00 - File Created
|
18 |
|
|
// Additional Comments: GPL License policies apply to the contents of this file.
|
19 |
|
|
//
|
20 |
|
|
//////////////////////////////////////////////////////////////////////////////////
|
21 |
|
|
|
22 |
|
|
/*
|
23 |
|
|
This module generates a high level on "isfalling" when "a" changes from high to low.
|
24 |
|
|
*/
|
25 |
|
|
module getfedge (
|
26 |
|
|
input clk,
|
27 |
|
|
input a,
|
28 |
|
|
output isfalling
|
29 |
|
|
);
|
30 |
|
|
|
31 |
|
|
reg sh = 1'b1;
|
32 |
|
|
assign isfalling = sh & ~a;
|
33 |
|
|
always @(posedge clk)
|
34 |
|
|
sh <= a;
|
35 |
|
|
endmodule
|
36 |
|
|
|
37 |
|
|
|
38 |
|
|
/*
|
39 |
|
|
This module implements a shared RAM controller. The Spartan 3 Starter Kit has two 256Kx16 SRAM chips.
|
40 |
|
|
It uses half the size of one of these chips (8 bit data bus instead of 16).
|
41 |
|
|
As the ZX Spectrum needs two independent memory banks, and bus cycles may happen to both at the same
|
42 |
|
|
time, it's necessary to emulate those two banks with one chip.
|
43 |
|
|
|
44 |
|
|
I tried a simple round-robin multiplexing, but didn't work as expected. This module is a bit more complicated
|
45 |
|
|
as it implements a first-come-first-serve approach, with fixed priority scheme when several petitions happen
|
46 |
|
|
at the same time.
|
47 |
|
|
|
48 |
|
|
Each bank can be up to 64Kx8, so implementing a 128K memory scheme is very easy (I hope so) using this module
|
49 |
|
|
and the external SRAM on board.
|
50 |
|
|
|
51 |
|
|
This may be my first 100% synchronous implementation for a FPGA (that is, only one clock and all the ff's are
|
52 |
|
|
activated at the same edge)
|
53 |
|
|
|
54 |
|
|
*/
|
55 |
|
|
module ram_controller (
|
56 |
|
|
input clk,
|
57 |
|
|
// Bank 1 (VRAM)
|
58 |
|
|
input [15:0] a1,
|
59 |
|
|
input cs1_n,
|
60 |
|
|
input oe1_n,
|
61 |
|
|
input we1_n,
|
62 |
|
|
input [7:0] din1,
|
63 |
|
|
output [7:0] dout1,
|
64 |
|
|
// Bank 2 (upper RAM)
|
65 |
|
|
input [15:0] a2,
|
66 |
|
|
input cs2_n,
|
67 |
|
|
input oe2_n,
|
68 |
|
|
input we2_n,
|
69 |
|
|
input [7:0] din2,
|
70 |
|
|
output [7:0] dout2,
|
71 |
|
|
// Outputs to actual SRAM on board
|
72 |
|
|
output [17:0] sa,
|
73 |
|
|
inout [7:0] sd,
|
74 |
|
|
output sramce,
|
75 |
|
|
output sramub,
|
76 |
|
|
output sramlb,
|
77 |
|
|
output sramoe,
|
78 |
|
|
output sramwe
|
79 |
|
|
);
|
80 |
|
|
|
81 |
|
|
// Permanently enable SRAM and set it to use only LSB
|
82 |
|
|
assign sramub = 1;
|
83 |
|
|
assign sramlb = 0;
|
84 |
|
|
assign sramce = 0;
|
85 |
|
|
assign sramoe = 0;
|
86 |
|
|
|
87 |
|
|
reg rsramwe = 1;
|
88 |
|
|
assign sramwe = rsramwe;
|
89 |
|
|
|
90 |
|
|
reg [17:0] rsa;
|
91 |
|
|
reg [7:0] rsd;
|
92 |
|
|
assign sa = rsa;
|
93 |
|
|
assign sd = rsd;
|
94 |
|
|
|
95 |
|
|
// set when there has been a high to low transition in the corresponding signal
|
96 |
|
|
wire bank1read, bank1write, bank2read, bank2write;
|
97 |
|
|
getfedge detectbank1read (clk, cs1_n | oe1_n, bank1read);
|
98 |
|
|
getfedge detectbank2read (clk, cs2_n | oe2_n, bank2read);
|
99 |
|
|
getfedge detectbank1write (clk, cs1_n | we1_n, bank1write);
|
100 |
|
|
getfedge detectbank2write (clk, cs2_n | we2_n, bank2write);
|
101 |
|
|
|
102 |
|
|
reg [15:0] ra1;
|
103 |
|
|
reg [15:0] ra2;
|
104 |
|
|
reg [7:0] rdin1;
|
105 |
|
|
reg [7:0] rdin2;
|
106 |
|
|
|
107 |
|
|
reg [7:0] rdout1;
|
108 |
|
|
assign dout1 = rdout1;
|
109 |
|
|
reg [7:0] rdout2;
|
110 |
|
|
assign dout2 = rdout2;
|
111 |
|
|
|
112 |
|
|
// ff's to store pending memory requests
|
113 |
|
|
reg pendingreadb1 = 0;
|
114 |
|
|
reg pendingwriteb1 = 0;
|
115 |
|
|
reg pendingreadb2 = 0;
|
116 |
|
|
reg pendingwriteb2 = 0;
|
117 |
|
|
|
118 |
|
|
// ff's to store current memory requests
|
119 |
|
|
reg reqreadb1 = 0;
|
120 |
|
|
reg reqreadb2 = 0;
|
121 |
|
|
reg reqwriteb1 = 0;
|
122 |
|
|
reg reqwriteb2 = 0;
|
123 |
|
|
|
124 |
|
|
reg state = 1;
|
125 |
|
|
always @(posedge clk) begin
|
126 |
|
|
// get requests from the two banks
|
127 |
|
|
if (bank1read) begin
|
128 |
|
|
ra1 <= a1;
|
129 |
|
|
pendingreadb1 <= 1;
|
130 |
|
|
pendingwriteb1 <= 0;
|
131 |
|
|
end
|
132 |
|
|
else if (bank1write) begin
|
133 |
|
|
ra1 <= a1;
|
134 |
|
|
rdin1 <= din1;
|
135 |
|
|
pendingwriteb1 <= 1;
|
136 |
|
|
pendingreadb1 <= 0;
|
137 |
|
|
end
|
138 |
|
|
if (bank2read) begin
|
139 |
|
|
ra2 <= a2;
|
140 |
|
|
pendingreadb2 <= 1;
|
141 |
|
|
pendingwriteb2 <= 0;
|
142 |
|
|
end
|
143 |
|
|
else if (bank2write) begin
|
144 |
|
|
ra2 <= a2;
|
145 |
|
|
rdin2 <= din2;
|
146 |
|
|
pendingwriteb2 <= 1;
|
147 |
|
|
pendingreadb2 <= 0;
|
148 |
|
|
end
|
149 |
|
|
|
150 |
|
|
// reads from bank1 have the higher priority, then writes to bank1,
|
151 |
|
|
// the reads from bank2, then writes from bank2.
|
152 |
|
|
// Reads and writes to bank2 are mutually exclusive, though, as only the CPU
|
153 |
|
|
// performs those operations. So they are with respect to bank1.
|
154 |
|
|
case (state)
|
155 |
|
|
|
156 |
|
|
if (reqreadb1 || reqwriteb1) begin
|
157 |
|
|
rsa <= {2'b00,ra1}; // operation to bank1 accepted. We put the memory address on the SRAM address bus
|
158 |
|
|
if (reqwriteb1) begin // if this is a write operation...
|
159 |
|
|
pendingwriteb1 <= 0; // accept it, and mark pending operation as cleared
|
160 |
|
|
rsd <= rdin1; // put the data to be written in the SRAM data bus
|
161 |
|
|
rsramwe <= 0; // pulse /WE in SRAM to begin write
|
162 |
|
|
end
|
163 |
|
|
else begin
|
164 |
|
|
pendingreadb1 <= 0; // else, this is a read operation...
|
165 |
|
|
rsd <= 8'bzzzzzzzz; // disconnect the output bus from the data register to the SRAM data bus, so
|
166 |
|
|
rsramwe <= 1; // we can read from the SRAM data bus itself. Deassert /WE to enable data output bus
|
167 |
|
|
end
|
168 |
|
|
state <= 1; // if either request has been accepted, proceed to next phase.
|
169 |
|
|
end
|
170 |
|
|
else if (reqreadb2 || reqwriteb2) begin // do the same with requests to bank 2...
|
171 |
|
|
rsa <= {2'b01,ra2};
|
172 |
|
|
if (reqwriteb2) begin
|
173 |
|
|
pendingwriteb2 <= 0;
|
174 |
|
|
rsd <= rdin2;
|
175 |
|
|
rsramwe <= 0;
|
176 |
|
|
end
|
177 |
|
|
else begin
|
178 |
|
|
pendingreadb2 <= 0;
|
179 |
|
|
rsd <= 8'bzzzzzzzz;
|
180 |
|
|
rsramwe <= 1;
|
181 |
|
|
end
|
182 |
|
|
state <= 1;
|
183 |
|
|
end
|
184 |
|
|
end
|
185 |
|
|
1 : begin
|
186 |
|
|
if (reqreadb1) begin // for read requests, read the SRAM data bus and store into the corresponding data output register
|
187 |
|
|
rdout1 <= sd;
|
188 |
|
|
end
|
189 |
|
|
else if (reqreadb2) begin
|
190 |
|
|
rdout2 <= sd;
|
191 |
|
|
end
|
192 |
|
|
if (reqwriteb1) begin // for write requests, deassert /WE, as writting has already been happened.
|
193 |
|
|
rsramwe <= 1;
|
194 |
|
|
end
|
195 |
|
|
else if (reqwriteb2) begin
|
196 |
|
|
rsramwe <= 1;
|
197 |
|
|
end
|
198 |
|
|
reqreadb1 <= pendingreadb1; // current request has finished, so update current requests with pending requests to serve the next one
|
199 |
|
|
reqreadb2 <= pendingreadb2;
|
200 |
|
|
reqwriteb1 <= pendingwriteb1;
|
201 |
|
|
reqwriteb2 <= pendingwriteb2;
|
202 |
|
|
if (pendingreadb1 || pendingreadb2 || pendingwriteb1 || pendingwriteb2)
|
203 |
|
|
state <= 0;
|
204 |
|
|
end
|
205 |
|
|
endcase
|
206 |
|
|
end
|
207 |
|
|
endmodule
|