1
- // A2N20v2-SDRAM - Tang Nano 20K SDRAM Version
1
+ // 2N20v2-SDRAM - Tang Nano 20K SDRAM implementation of Apple II memory
2
+ //
3
+ // (c) 2023,2024 Ed Anuff <ed@a2fpga.com>
4
+ //
5
+ // Permission to use, copy, modify, and/or distribute this software for any
6
+ // purpose with or without fee is hereby granted, provided that the above
7
+ // copyright notice and this permission notice appear in all copies.
8
+ //
9
+ // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+ //
17
+ // Description:
2
18
//
3
19
// Handle the writing of data to the shadow memory copy of the Apple II's
4
- // memory that is kept in the FPGA's SDRAM.
20
+ // memory that is kept in the FPGA's SDRAM. IIgs SHRG memory is in blockram.
5
21
//
6
22
7
23
module apple_memory # (
@@ -17,7 +33,7 @@ module apple_memory #(
17
33
input video_bank_i,
18
34
input video_rd_i,
19
35
output [31 : 0 ] video_data_o,
20
-
36
+
21
37
input vgc_active_i,
22
38
input [12 : 0 ] vgc_address_i,
23
39
input vgc_rd_i,
@@ -29,89 +45,77 @@ module apple_memory #(
29
45
wire read_strobe = a2bus_if.rw_n && a2bus_if.data_in_strobe;
30
46
31
47
// II Soft switches
32
- reg switches_ii_r [8 ];
33
- assign a2mem_if.TEXT_MODE = switches_ii_r [0 ];
34
- assign a2mem_if.MIXED_MODE = switches_ii_r [1 ];
35
- assign a2mem_if.PAGE2 = switches_ii_r [2 ];
36
- assign a2mem_if.HIRES_MODE = switches_ii_r [3 ];
37
- assign a2mem_if.AN0 = switches_ii_r [4 ];
38
- assign a2mem_if.AN1 = switches_ii_r [5 ];
39
- assign a2mem_if.AN2 = switches_ii_r [6 ];
40
- assign a2mem_if.AN3 = switches_ii_r [7 ];
48
+ reg SWITCHES_II [8 ];
49
+ assign a2mem_if.TEXT_MODE = SWITCHES_II [0 ];
50
+ assign a2mem_if.MIXED_MODE = SWITCHES_II [1 ];
51
+ assign a2mem_if.PAGE2 = SWITCHES_II [2 ];
52
+ assign a2mem_if.HIRES_MODE = SWITCHES_II [3 ];
53
+ assign a2mem_if.AN0 = SWITCHES_II [4 ];
54
+ assign a2mem_if.AN1 = SWITCHES_II [5 ];
55
+ assign a2mem_if.AN2 = SWITCHES_II [6 ];
56
+ assign a2mem_if.AN3 = SWITCHES_II [7 ];
41
57
42
58
// ][e auxilary switches
43
- reg switches_iie_r[8 ];
44
- assign a2mem_if.STORE80 = switches_iie_r[0 ];
45
- assign a2mem_if.RAMRD = switches_iie_r[1 ];
46
- assign a2mem_if.RAMWRT = switches_iie_r[2 ];
47
- assign a2mem_if.CXROM = switches_iie_r[3 ];
48
- assign a2mem_if.ALTZP = switches_iie_r[4 ];
49
- assign a2mem_if.C3ROM = switches_iie_r[5 ];
50
- assign a2mem_if.COL80 = switches_iie_r[6 ];
51
- assign a2mem_if.ALTCHAR = switches_iie_r[7 ];
52
-
53
- reg [3 : 0 ] text_color_r;
54
- reg [3 : 0 ] background_color_r;
55
- reg [3 : 0 ] border_color_r;
56
- reg monochrome_mode_r;
57
- reg monochrome_dhires_mode_r;
58
- reg shrg_mode_r;
59
-
60
- assign a2mem_if.TEXT_COLOR = text_color_r;
61
- assign a2mem_if.BACKGROUND_COLOR = background_color_r;
62
- assign a2mem_if.BORDER_COLOR = border_color_r;
63
- assign a2mem_if.MONOCHROME_MODE = monochrome_mode_r;
64
- assign a2mem_if.MONOCHROME_DHIRES_MODE = monochrome_dhires_mode_r;
65
- assign a2mem_if.SHRG_MODE = shrg_mode_r;
59
+ reg SWITCHES_IIE [8 ];
60
+ assign a2mem_if.STORE80 = SWITCHES_IIE [0 ];
61
+ assign a2mem_if.RAMRD = SWITCHES_IIE [1 ];
62
+ assign a2mem_if.RAMWRT = SWITCHES_IIE [2 ];
63
+ assign a2mem_if.CXROM = SWITCHES_IIE [3 ];
64
+ assign a2mem_if.ALTZP = SWITCHES_IIE [4 ];
65
+ assign a2mem_if.C3ROM = SWITCHES_IIE [5 ];
66
+ assign a2mem_if.COL80 = SWITCHES_IIE [6 ];
67
+ assign a2mem_if.ALTCHAR = SWITCHES_IIE [7 ];
66
68
67
69
// capture the soft switches
68
70
always @ (posedge a2bus_if.clk_logic or negedge a2bus_if.system_reset_n) begin
69
71
if (! a2bus_if.system_reset_n) begin
70
- switches_ii_r <= '{ 1'b1 , 1'b1 , 1'b0 , 1'b0 , 1'b0 , 1'b0 , 1'b0 , 1'b1 } ;
72
+ SWITCHES_II <= '{ 1'b1 , 1'b1 , 1'b0 , 1'b0 , 1'b0 , 1'b0 , 1'b0 , 1'b1 } ;
71
73
end else if ((a2bus_if.phi1_posedge) && (a2bus_if.addr[15 : 4 ] == 12'hC05 ) && ! a2bus_if.m2sel_n)
72
- switches_ii_r [a2bus_if.addr[3 : 1 ]] <= a2bus_if.addr[0 ];
74
+ SWITCHES_II [a2bus_if.addr[3 : 1 ]] <= a2bus_if.addr[0 ];
73
75
end
74
76
75
77
always @ (posedge a2bus_if.clk_logic or negedge a2bus_if.system_reset_n) begin
76
78
if (! a2bus_if.system_reset_n) begin
77
- switches_iie_r <= '{ 8 { 1'b0 }} ;
79
+ SWITCHES_IIE <= '{ 8 { 1'b0 }} ;
78
80
end else if (! a2bus_if.rw_n && (a2bus_if.phi1_posedge) && (a2bus_if.addr[15 : 4 ] == 12'hC00 ) && ! a2bus_if.m2sel_n)
79
- switches_iie_r [a2bus_if.addr[3 : 1 ]] <= a2bus_if.addr[0 ];
81
+ SWITCHES_IIE [a2bus_if.addr[3 : 1 ]] <= a2bus_if.addr[0 ];
80
82
end
81
83
82
84
always @ (posedge a2bus_if.clk_logic or negedge a2bus_if.device_reset_n) begin
83
85
if (! a2bus_if.device_reset_n) begin
84
- background_color_r <= 4'h0 ;
85
- text_color_r <= 4'hF ;
86
+ a2mem_if. BACKGROUND_COLOR <= 4'h0 ;
87
+ a2mem_if. TEXT_COLOR <= 4'hF ;
86
88
end else if (write_strobe && (a2bus_if.addr == 16'hC022 )) begin
87
- background_color_r <= a2bus_if.data[3 : 0 ];
88
- text_color_r <= a2bus_if.data[7 : 4 ];
89
+ a2mem_if. BACKGROUND_COLOR <= a2bus_if.data[3 : 0 ];
90
+ a2mem_if. TEXT_COLOR <= a2bus_if.data[7 : 4 ];
89
91
end
90
92
end
91
93
92
94
always @ (posedge a2bus_if.clk_logic or negedge a2bus_if.device_reset_n) begin
93
95
if (! a2bus_if.device_reset_n) begin
94
- border_color_r <= 4'h0 ;
96
+ a2mem_if. BORDER_COLOR <= 4'h0 ;
95
97
end else if (write_strobe && (a2bus_if.addr == 16'hC034 )) begin
96
- border_color_r <= a2bus_if.data[3 : 0 ];
98
+ a2mem_if. BORDER_COLOR <= a2bus_if.data[3 : 0 ];
97
99
end
98
100
end
99
101
100
102
always @ (posedge a2bus_if.clk_logic or negedge a2bus_if.system_reset_n) begin
101
103
if (! a2bus_if.system_reset_n) begin
102
- monochrome_mode_r <= 1'b0 ;
104
+ a2mem_if. MONOCHROME_MODE <= 1'b0 ;
103
105
end else if (write_strobe && (a2bus_if.addr == 16'hC021 )) begin
104
- monochrome_mode_r <= a2bus_if.data[7 ];
106
+ a2mem_if. MONOCHROME_MODE <= a2bus_if.data[7 ];
105
107
end
106
108
end
107
109
108
110
always @ (posedge a2bus_if.clk_logic or negedge a2bus_if.system_reset_n) begin
109
111
if (! a2bus_if.system_reset_n) begin
110
- monochrome_dhires_mode_r <= 1'b0 ;
111
- shrg_mode_r <= 1'b0 ;
112
+ a2mem_if.MONOCHROME_DHIRES_MODE <= 1'b0 ;
113
+ a2mem_if.LINEARIZE_MODE <= 1'b0 ;
114
+ a2mem_if.SHRG_MODE <= 1'b0 ;
112
115
end else if (write_strobe && (a2bus_if.addr == 16'hC029 )) begin
113
- monochrome_dhires_mode_r <= a2bus_if.data[5 ];
114
- shrg_mode_r <= a2bus_if.data[7 ];
116
+ a2mem_if.MONOCHROME_DHIRES_MODE <= a2bus_if.data[5 ];
117
+ a2mem_if.LINEARIZE_MODE <= a2bus_if.data[6 ] | a2bus_if.data[7 ];
118
+ a2mem_if.SHRG_MODE <= a2bus_if.data[7 ];
115
119
end
116
120
end
117
121
@@ -138,10 +142,9 @@ module apple_memory #(
138
142
assign a2mem_if.keycode = keycode_r;
139
143
assign a2mem_if.keypress_strobe = keypress_strobe_r;
140
144
141
- reg aux_mem_r;
145
+ logic aux_mem_r;
142
146
143
- always @ (* )
144
- begin : aux_ctrl
147
+ always_comb begin
145
148
aux_mem_r = 1'b0 ;
146
149
if (a2bus_if.addr[15 : 9 ] == 7'b0000000 | a2bus_if.addr[15 : 14 ] == 2'b11 ) // Page 00,01,C0-FF
147
150
aux_mem_r = a2mem_if.ALTZP ;
@@ -166,19 +169,113 @@ module apple_memory #(
166
169
167
170
wire E1 = aux_mem_r || a2bus_if.m2b0;
168
171
172
+ wire [31 : 0 ] write_word = { a2bus_if.data, a2bus_if.data, a2bus_if.data, a2bus_if.data} ;
173
+
169
174
// Apple II bus address ranges
170
175
wire bus_addr_0400_0BFF = a2bus_if.addr[15 : 10 ] inside { 6'b000001 , 3'b000010 } ;
171
176
wire bus_addr_2000_5FFF = a2bus_if.addr[15 : 13 ] inside { 3'b001 , 3'b010 } ;
172
177
wire bus_addr_6000_9FFF = a2bus_if.addr[15 : 13 ] inside { 3'b011 , 3'b100 } ;
173
178
wire bus_addr_2000_9FFF = bus_addr_2000_5FFF || bus_addr_6000_9FFF;
174
179
180
+ wire [14 : 0 ] hires_write_offset = 15 '({ 3 '(a2bus_if.addr[15 : 13 ] - 1'b1 ), a2bus_if.addr[12 : 0 ]} );
181
+
182
+ wire [31 : 0 ] hires_data_aux;
183
+
184
+ function automatic [31 : 0 ] interleave_mux (input hi, input [31 : 0 ] data_a, input [31 : 0 ] data_b);
185
+ logic [31 : 0 ] result = 0 ;
186
+ if (hi) result = { data_b[31 : 24 ], data_a[31 : 24 ], data_b[23 : 16 ], data_a[23 : 16 ]} ;
187
+ else result = { data_b[15 : 8 ], data_a[15 : 8 ], data_b[7 : 0 ], data_a[7 : 0 ]} ;
188
+ return result;
189
+ endfunction
190
+
191
+ wire [3 : 0 ] hires_byte_enable = 4 '(1 << hires_write_offset[1 : 0 ]);
192
+
193
+ // Aux memory bank, linear
194
+
195
+ // The aux memory bank for hires is 16KB, but but when VGC_MEMORY is set, an additional 16KB is added
196
+
197
+ // Set up reads and combine ouputs for VGC
198
+
199
+ wire [11 : 0 ] hires_aux_read_offset = vgc_address_i[12 : 1 ];
200
+
201
+ wire [31 : 0 ] hires_data_aux_6000_9FFF;
202
+
203
+ assign vgc_data_o = vgc_active_i ? interleave_mux (vgc_address_i[0 ], hires_data_aux, hires_data_aux_6000_9FFF) : 32'b0 ;
204
+
205
+ // Set up writes
206
+
207
+ logic write_enable_aux_2000_5FFF;
208
+ logic write_enable_aux_6000_9FFF;
209
+ logic [11 : 0 ] write_offset_aux_2000_5FFF;
210
+ logic [11 : 0 ] write_offset_aux_6000_9FFF;
211
+ logic [3 : 0 ] hires_byte_enable_aux_2000_5FFF;
212
+ logic [3 : 0 ] hires_byte_enable_aux_6000_9FFF;
213
+
214
+ always_comb begin
215
+ write_enable_aux_2000_5FFF = 1'b0 ;
216
+ write_offset_aux_2000_5FFF = 12'b0 ;
217
+ hires_byte_enable_aux_2000_5FFF = 4'b0 ;
218
+
219
+ write_enable_aux_6000_9FFF = 1'b0 ;
220
+ write_offset_aux_6000_9FFF = 12'b0 ;
221
+ hires_byte_enable_aux_6000_9FFF = 4'b0 ;
222
+
223
+ if (a2mem_if.LINEARIZE_MODE ) begin
224
+ write_enable_aux_2000_5FFF = write_strobe && bus_addr_2000_9FFF && E1 ;
225
+ write_offset_aux_2000_5FFF = hires_write_offset[14 : 3 ];
226
+ hires_byte_enable_aux_2000_5FFF = hires_write_offset[0 ] ? 4'b0 : 4 '(1 << hires_write_offset[2 : 1 ]);
227
+
228
+ write_enable_aux_6000_9FFF = write_strobe && bus_addr_2000_9FFF && E1 ;
229
+ write_offset_aux_6000_9FFF = hires_write_offset[14 : 3 ];
230
+ hires_byte_enable_aux_6000_9FFF = hires_write_offset[0 ] ? 4 '(1 << hires_write_offset[2 : 1 ]) : 4'b0 ;
231
+
232
+ end else begin
233
+ if (bus_addr_2000_5FFF) begin
234
+ write_enable_aux_2000_5FFF = write_strobe && bus_addr_2000_5FFF && E1 ;
235
+ write_offset_aux_2000_5FFF = hires_write_offset[13 : 2 ];
236
+ hires_byte_enable_aux_2000_5FFF = hires_byte_enable;
237
+ end else if (bus_addr_6000_9FFF) begin
238
+ write_enable_aux_6000_9FFF = write_strobe && bus_addr_6000_9FFF && E1 ;
239
+ write_offset_aux_6000_9FFF = hires_write_offset[13 : 2 ];
240
+ hires_byte_enable_aux_6000_9FFF = hires_byte_enable;
241
+ end
242
+ end
243
+
244
+ end
245
+
246
+ sdpram32 # (
247
+ .ADDR_WIDTH (12 )
248
+ ) hires_aux_2000_5FFF (
249
+ .clk (a2bus_if.clk_logic),
250
+ .write_addr (write_offset_aux_2000_5FFF),
251
+ .write_data (write_word),
252
+ .write_enable (write_enable_aux_2000_5FFF),
253
+ .byte_enable (hires_byte_enable_aux_2000_5FFF),
254
+ .read_addr (hires_aux_read_offset),
255
+ .read_enable (1'b1 ),
256
+ .read_data (hires_data_aux)
257
+ );
258
+
259
+ sdpram32 # (
260
+ .ADDR_WIDTH (12 )
261
+ ) hires_aux_6000_9FFF (
262
+ .clk (a2bus_if.clk_logic),
263
+ .write_addr (write_offset_aux_6000_9FFF),
264
+ .write_data (write_word),
265
+ .write_enable (write_enable_aux_6000_9FFF),
266
+ .byte_enable (hires_byte_enable_aux_6000_9FFF),
267
+ .read_addr (hires_aux_read_offset),
268
+ .read_enable (1'b1 ),
269
+ .read_data (hires_data_aux_6000_9FFF)
270
+ );
271
+
272
+ // SDRAM interace
273
+
175
274
wire write_en = ! a2bus_if.rw_n &&
176
275
a2bus_if.data_in_strobe &&
177
276
(SHADOW_ALL_MEMORY || bus_addr_2000_5FFF || bus_addr_0400_0BFF) &&
178
277
! a2bus_if.m2sel_n;
179
278
180
- wire [31 : 0 ] write_word = { a2bus_if.data, a2bus_if.data, a2bus_if.data, a2bus_if.data} ;
181
-
182
279
assign main_mem_if.rd = 1'b0 ;
183
280
assign main_mem_if.wr = write_en;
184
281
assign main_mem_if.addr = { 6'b0 , a2bus_if.addr[15 : 1 ]} ;
@@ -192,19 +289,4 @@ module apple_memory #(
192
289
assign video_mem_if.byte_en = 4'b1111 ;
193
290
assign video_data_o = video_mem_if.q;
194
291
195
- wire [14 : 0 ] hires_write_offset = 15 '({ 3 '(a2bus_if.addr[15 : 13 ] - 1'b1 ), a2bus_if.addr[12 : 0 ]} );
196
-
197
- sdpram32 # (
198
- .ADDR_WIDTH (13 )
199
- ) hires_aux (
200
- .clk (a2bus_if.clk_logic),
201
- .write_addr (hires_write_offset[14 : 2 ]),
202
- .write_data (write_word),
203
- .write_enable (write_strobe && bus_addr_2000_9FFF && E1 ),
204
- .byte_enable (4 '(1 << hires_write_offset[1 : 0 ])),
205
- .read_addr (vgc_address_i),
206
- .read_enable (1'b1 ),
207
- .read_data (vgc_data_o)
208
- );
209
-
210
292
endmodule
0 commit comments