-
Notifications
You must be signed in to change notification settings - Fork 3
/
via6522.vhd
705 lines (592 loc) · 26.4 KB
/
via6522.vhd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
-------------------------------------------------------------------------------
-- Title : VIA 6522
-------------------------------------------------------------------------------
-- Author : Gideon Zweijtzer <gideon.zweijtzer@gmail.com>
-------------------------------------------------------------------------------
-- Description: This module implements the 6522 VIA chip.
-- A LOT OF REVERSE ENGINEERING has been done to make this module
-- as accurate as it is now. Thanks to gyurco for ironing out some
-- differences that were left unnoticed.
-------------------------------------------------------------------------------
-- License: GPL 3.0 - Free to use, distribute and change to your own needs.
-- Leaving a reference to the author will be highly appreciated.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity via6522 is
port (
clock : in std_logic;
rising : in std_logic;
falling : in std_logic;
reset : in std_logic;
addr : in std_logic_vector(3 downto 0);
wen : in std_logic;
ren : in std_logic;
data_in : in std_logic_vector(7 downto 0);
data_out : out std_logic_vector(7 downto 0);
phi2_ref : out std_logic;
-- pio --
port_a_o : out std_logic_vector(7 downto 0);
port_a_t : out std_logic_vector(7 downto 0);
port_a_i : in std_logic_vector(7 downto 0);
port_b_o : out std_logic_vector(7 downto 0);
port_b_t : out std_logic_vector(7 downto 0);
port_b_i : in std_logic_vector(7 downto 0);
-- handshake pins
ca1_i : in std_logic;
ca2_o : out std_logic;
ca2_i : in std_logic;
ca2_t : out std_logic;
cb1_o : out std_logic;
cb1_i : in std_logic;
cb1_t : out std_logic;
cb2_o : out std_logic;
cb2_i : in std_logic;
cb2_t : out std_logic;
irq : out std_logic );
end via6522;
architecture Gideon of via6522 is
type pio_t is
record
pra : std_logic_vector(7 downto 0);
ddra : std_logic_vector(7 downto 0);
prb : std_logic_vector(7 downto 0);
ddrb : std_logic_vector(7 downto 0);
end record;
constant pio_default : pio_t := (others => (others => '0'));
constant latch_reset_pattern : std_logic_vector(15 downto 0) := X"5550";
signal last_data : std_logic_vector(7 downto 0) := X"55";
signal pio_i : pio_t;
signal port_a_c : std_logic_vector(7 downto 0) := (others => '0');
signal port_b_c : std_logic_vector(7 downto 0) := (others => '0');
signal irq_mask : std_logic_vector(6 downto 0) := (others => '0');
signal irq_flags : std_logic_vector(6 downto 0) := (others => '0');
signal irq_events : std_logic_vector(6 downto 0) := (others => '0');
signal irq_out : std_logic;
signal timer_a_latch : std_logic_vector(15 downto 0) := latch_reset_pattern;
signal timer_b_latch : std_logic_vector(15 downto 0) := latch_reset_pattern;
signal timer_a_count : std_logic_vector(15 downto 0) := latch_reset_pattern;
signal timer_b_count : std_logic_vector(15 downto 0) := latch_reset_pattern;
signal timer_a_out : std_logic;
signal timer_b_tick : std_logic;
signal acr, pcr : std_logic_vector(7 downto 0) := X"00";
signal shift_reg : std_logic_vector(7 downto 0) := X"00";
signal serport_en : std_logic;
signal ser_cb2_o : std_logic;
signal hs_cb2_o : std_logic;
signal cb1_t_int : std_logic;
signal cb1_o_int : std_logic;
signal cb2_t_int : std_logic;
signal cb2_o_int : std_logic;
alias ca2_event : std_logic is irq_events(0);
alias ca1_event : std_logic is irq_events(1);
alias serial_event : std_logic is irq_events(2);
alias cb2_event : std_logic is irq_events(3);
alias cb1_event : std_logic is irq_events(4);
alias timer_b_event : std_logic is irq_events(5);
alias timer_a_event : std_logic is irq_events(6);
alias ca2_flag : std_logic is irq_flags(0);
alias ca1_flag : std_logic is irq_flags(1);
alias serial_flag : std_logic is irq_flags(2);
alias cb2_flag : std_logic is irq_flags(3);
alias cb1_flag : std_logic is irq_flags(4);
alias timer_b_flag : std_logic is irq_flags(5);
alias timer_a_flag : std_logic is irq_flags(6);
alias tmr_a_output_en : std_logic is acr(7);
alias tmr_a_freerun : std_logic is acr(6);
alias tmr_b_count_mode : std_logic is acr(5);
alias shift_dir : std_logic is acr(4);
alias shift_clk_sel : std_logic_vector(1 downto 0) is acr(3 downto 2);
alias shift_mode_control : std_logic_vector(2 downto 0) is acr(4 downto 2);
alias pb_latch_en : std_logic is acr(1);
alias pa_latch_en : std_logic is acr(0);
alias cb2_is_output : std_logic is pcr(7);
alias cb2_edge_select : std_logic is pcr(6); -- for when CB2 is input
alias cb2_no_irq_clr : std_logic is pcr(5); -- for when CB2 is input
alias cb2_out_mode : std_logic_vector(1 downto 0) is pcr(6 downto 5);
alias cb1_edge_select : std_logic is pcr(4);
alias ca2_is_output : std_logic is pcr(3);
alias ca2_edge_select : std_logic is pcr(2); -- for when CA2 is input
alias ca2_no_irq_clr : std_logic is pcr(1); -- for when CA2 is input
alias ca2_out_mode : std_logic_vector(1 downto 0) is pcr(2 downto 1);
alias ca1_edge_select : std_logic is pcr(0);
signal ira, irb : std_logic_vector(7 downto 0) := (others => '0');
signal write_t1c_l : std_logic;
signal write_t1c_h : std_logic;
signal write_t2c_h : std_logic;
signal ca1_c, ca2_c : std_logic;
signal cb1_c, cb2_c : std_logic;
signal ca1_d, ca2_d : std_logic;
signal cb1_d, cb2_d : std_logic;
signal ca2_handshake_o : std_logic;
signal ca2_pulse_o : std_logic;
signal cb2_handshake_o : std_logic;
signal cb2_pulse_o : std_logic;
signal shift_active : std_logic;
begin
irq <= irq_out;
write_t1c_l <= '1' when (addr = X"4" or addr = x"6") and wen='1' and falling = '1' else '0';
write_t1c_h <= '1' when addr = X"5" and wen='1' and falling = '1' else '0';
write_t2c_h <= '1' when addr = X"9" and wen='1' and falling = '1' else '0';
ca1_event <= (ca1_c xor ca1_d) and (ca1_d xor ca1_edge_select);
ca2_event <= (ca2_c xor ca2_d) and (ca2_d xor ca2_edge_select);
cb1_event <= (cb1_c xor cb1_d) and (cb1_d xor cb1_edge_select);
cb2_event <= (cb2_c xor cb2_d) and (cb2_d xor cb2_edge_select);
ca2_t <= ca2_is_output;
cb2_t_int <= cb2_is_output when serport_en='0' else shift_dir;
cb2_o_int <= hs_cb2_o when serport_en='0' else ser_cb2_o;
cb1_t <= cb1_t_int;
cb1_o <= cb1_o_int;
cb2_t <= cb2_t_int;
cb2_o <= cb2_o_int;
with ca2_out_mode select ca2_o <=
ca2_handshake_o when "00",
ca2_pulse_o when "01",
'0' when "10",
'1' when others;
with cb2_out_mode select hs_cb2_o <=
cb2_handshake_o when "00",
cb2_pulse_o when "01",
'0' when "10",
'1' when others;
process(irq_flags, irq_mask)
begin
if (irq_flags and irq_mask) = "0000000" then
irq_out <= '0';
else
irq_out <= '1';
end if;
end process;
process(clock)
begin
if rising_edge(clock) then
if rising = '1' then
phi2_ref <= '1';
elsif falling = '1' then
phi2_ref <= '0';
end if;
end if;
end process;
process(clock)
begin
if rising_edge(clock) then
-- CA1/CA2/CB1/CB2 edge detect flipflops
ca1_c <= To_X01(ca1_i);
ca2_c <= To_X01(ca2_i);
if cb1_t_int = '0' then
cb1_c <= To_X01(cb1_i);
else
cb1_c <= cb1_o_int;
end if;
if cb2_t_int = '0' then
cb2_c <= To_X01(cb2_i);
else
cb2_c <= cb2_o_int;
end if;
ca1_d <= ca1_c;
ca2_d <= ca2_c;
cb1_d <= cb1_c;
cb2_d <= cb2_c;
-- input registers
port_a_c <= port_a_i;
port_b_c <= port_b_i;
-- input latch emulation
if pa_latch_en = '0' or ca1_event = '1' then
ira <= port_a_c;
end if;
if pb_latch_en = '0' or cb1_event = '1' then
irb <= port_b_c;
end if;
-- CA2 logic
if ca1_event = '1' then
ca2_handshake_o <= '1';
elsif (ren = '1' or wen = '1') and addr = X"1" and falling = '1' then
ca2_handshake_o <= '0';
end if;
if falling = '1' then
if (ren = '1' or wen = '1') and addr = X"1" then
ca2_pulse_o <= '0';
else
ca2_pulse_o <= '1';
end if;
end if;
-- CB2 logic
if cb1_event = '1' then
cb2_handshake_o <= '1';
elsif (ren = '1' or wen = '1') and addr = X"0" and falling = '1' then
cb2_handshake_o <= '0';
end if;
if falling = '1' then
if (ren = '1' or wen = '1') and addr = X"0" then
cb2_pulse_o <= '0';
else
cb2_pulse_o <= '1';
end if;
end if;
-- Interrupt logic
irq_flags <= irq_flags or irq_events;
-- Writes --
if wen='1' and falling = '1' then
last_data <= data_in;
case addr is
when X"0" => -- ORB
pio_i.prb <= data_in;
if cb2_no_irq_clr='0' then
cb2_flag <= '0';
end if;
cb1_flag <= '0';
when X"1" => -- ORA
pio_i.pra <= data_in;
if ca2_no_irq_clr='0' then
ca2_flag <= '0';
end if;
ca1_flag <= '0';
when X"2" => -- DDRB
pio_i.ddrb <= data_in;
when X"3" => -- DDRA
pio_i.ddra <= data_in;
when X"4" => -- TA LO counter (write=latch)
timer_a_latch(7 downto 0) <= data_in;
when X"5" => -- TA HI counter
timer_a_latch(15 downto 8) <= data_in;
timer_a_flag <= '0';
when X"6" => -- TA LO latch
timer_a_latch(7 downto 0) <= data_in;
when X"7" => -- TA HI latch
timer_a_latch(15 downto 8) <= data_in;
timer_a_flag <= '0';
when X"8" => -- TB LO latch
timer_b_latch(7 downto 0) <= data_in;
when X"9" => -- TB HI counter
timer_b_flag <= '0';
when X"A" => -- Serial port
serial_flag <= '0';
when X"B" => -- ACR (Auxiliary Control Register)
acr <= data_in;
when X"C" => -- PCR (Peripheral Control Register)
pcr <= data_in;
when X"D" => -- IFR
irq_flags <= irq_flags and not data_in(6 downto 0);
when X"E" => -- IER
if data_in(7)='1' then -- set
irq_mask <= irq_mask or data_in(6 downto 0);
else -- clear
irq_mask <= irq_mask and not data_in(6 downto 0);
end if;
when X"F" => -- ORA no handshake
pio_i.pra <= data_in;
when others =>
null;
end case;
end if;
-- Reads - Output only --
data_out <= X"00";
case addr is
when X"0" => -- ORB
--Port B reads its own output register for pins set to output.
data_out <= (pio_i.prb and pio_i.ddrb) or (irb and not pio_i.ddrb);
if tmr_a_output_en='1' then
data_out(7) <= timer_a_out;
end if;
when X"1" => -- ORA
data_out <= ira;
when X"2" => -- DDRB
data_out <= pio_i.ddrb;
when X"3" => -- DDRA
data_out <= pio_i.ddra;
when X"4" => -- TA LO counter
data_out <= timer_a_count(7 downto 0);
when X"5" => -- TA HI counter
data_out <= timer_a_count(15 downto 8);
when X"6" => -- TA LO latch
data_out <= timer_a_latch(7 downto 0);
when X"7" => -- TA HI latch
data_out <= timer_a_latch(15 downto 8);
when X"8" => -- TA LO counter
data_out <= timer_b_count(7 downto 0);
when X"9" => -- TA HI counter
data_out <= timer_b_count(15 downto 8);
when X"A" => -- SR
data_out <= shift_reg;
when X"B" => -- ACR
data_out <= acr;
when X"C" => -- PCR
data_out <= pcr;
when X"D" => -- IFR
data_out <= irq_out & irq_flags;
when X"E" => -- IER
data_out <= '1' & irq_mask;
when X"F" => -- ORA
data_out <= ira;
when others =>
null;
end case;
-- Read actions --
if ren = '1' and falling = '1' then
case addr is
when X"0" => -- ORB
if cb2_no_irq_clr='0' then
cb2_flag <= '0';
end if;
cb1_flag <= '0';
when X"1" => -- ORA
if ca2_no_irq_clr='0' then
ca2_flag <= '0';
end if;
ca1_flag <= '0';
when X"4" => -- TA LO counter
timer_a_flag <= '0';
when X"8" => -- TB LO counter
timer_b_flag <= '0';
when X"A" => -- SR
serial_flag <= '0';
when others =>
null;
end case;
end if;
if reset='1' then
pio_i <= pio_default;
irq_mask <= (others => '0');
irq_flags <= (others => '0');
acr <= (others => '0');
pcr <= (others => '0');
ca2_handshake_o <= '1';
ca2_pulse_o <= '1';
cb2_handshake_o <= '1';
cb2_pulse_o <= '1';
timer_a_latch <= latch_reset_pattern;
timer_b_latch <= latch_reset_pattern;
end if;
end if;
end process;
-- PIO Out select --
port_a_o <= pio_i.pra;
port_b_o(6 downto 0) <= pio_i.prb(6 downto 0);
port_b_o(7) <= pio_i.prb(7) when tmr_a_output_en='0' else timer_a_out;
port_a_t <= pio_i.ddra;
port_b_t(6 downto 0) <= pio_i.ddrb(6 downto 0);
port_b_t(7) <= pio_i.ddrb(7) or tmr_a_output_en;
-- Timer A
tmr_a: block
signal timer_a_reload : std_logic;
signal timer_a_toggle : std_logic;
signal timer_a_may_interrupt : std_logic;
begin
process(clock)
begin
if rising_edge(clock) then
if falling = '1' then
-- always count, or load
if timer_a_reload = '1' then
timer_a_count <= timer_a_latch;
if write_t1c_l = '1' then
timer_a_count(7 downto 0) <= data_in;
end if;
timer_a_reload <= '0';
timer_a_may_interrupt <= timer_a_may_interrupt and tmr_a_freerun;
else
if timer_a_count = X"0000" then
-- generate an event if we were triggered
timer_a_reload <= '1';
end if;
--Timer coutinues to count in both free run and one shot.
timer_a_count <= timer_a_count - X"0001";
end if;
end if;
if rising = '1' then
if timer_a_event = '1' and tmr_a_output_en = '1' then
timer_a_toggle <= not timer_a_toggle;
end if;
end if;
if write_t1c_h = '1' then
timer_a_may_interrupt <= '1';
timer_a_toggle <= not tmr_a_output_en;
timer_a_count <= data_in & timer_a_latch(7 downto 0);
timer_a_reload <= '0';
end if;
if reset='1' then
timer_a_may_interrupt <= '0';
timer_a_toggle <= '1';
timer_a_count <= latch_reset_pattern;
timer_a_reload <= '0';
end if;
end if;
end process;
timer_a_out <= timer_a_toggle;
timer_a_event <= rising and timer_a_reload and timer_a_may_interrupt;
end block tmr_a;
-- Timer B
tmr_b: block
signal timer_b_reload_lo : std_logic;
signal timer_b_oneshot_trig : std_logic;
signal timer_b_timeout : std_logic;
signal pb6_c, pb6_d : std_logic;
begin
process(clock)
variable timer_b_decrement : std_logic;
begin
if rising_edge(clock) then
timer_b_decrement := '0';
if rising = '1' then
pb6_c <= To_X01(port_b_i(6));
pb6_d <= pb6_c;
end if;
if falling = '1' then
timer_b_timeout <= '0';
timer_b_tick <= '0';
if tmr_b_count_mode = '1' then
if (pb6_d='1' and pb6_c='0') then
timer_b_decrement := '1';
end if;
else -- one shot or used for shift register
timer_b_decrement := '1';
end if;
if timer_b_decrement = '1' then
if timer_b_count = X"0000" then
if timer_b_oneshot_trig = '1' then
timer_b_oneshot_trig <= '0';
timer_b_timeout <= '1';
end if;
end if;
if timer_b_count(7 downto 0) = X"00" then
case shift_mode_control is
when "001" | "101" | "100" =>
timer_b_reload_lo <= '1';
timer_b_tick <= '1';
when others =>
null;
end case;
end if;
timer_b_count <= timer_b_count - X"0001";
end if;
if timer_b_reload_lo = '1' then
timer_b_count(7 downto 0) <= timer_b_latch(7 downto 0);
timer_b_reload_lo <= '0';
end if;
end if;
if write_t2c_h = '1' then
timer_b_count <= data_in & timer_b_latch(7 downto 0);
timer_b_oneshot_trig <= '1';
end if;
if reset='1' then
timer_b_count <= latch_reset_pattern;
timer_b_reload_lo <= '0';
timer_b_oneshot_trig <= '0';
end if;
end if;
end process;
timer_b_event <= rising and timer_b_timeout;
end block tmr_b;
ser: block
signal trigger_serial: std_logic;
signal shift_clock_d : std_logic;
signal shift_clock : std_logic;
signal shift_tick_r : std_logic;
signal shift_tick_f : std_logic;
signal shift_timer_tick : std_logic;
signal cb2_c : std_logic := '0';
signal bit_cnt : integer range 0 to 7;
signal shift_pulse : std_logic;
begin
process(shift_active, timer_b_tick, shift_clk_sel, shift_clock, shift_clock_d, shift_timer_tick)
begin
case shift_clk_sel is
when "10" =>
shift_pulse <= '1';
when "00"|"01" =>
shift_pulse <= shift_timer_tick;
when others =>
shift_pulse <= shift_clock and not shift_clock_d;
end case;
if shift_active = '0' then
-- Mode 0 still loads the shift register to external pulse (MMBEEB SD-Card interface uses this)
if shift_mode_control = "000" then
shift_pulse <= shift_clock and not shift_clock_d;
else
shift_pulse <= '0';
end if;
end if;
end process;
process(clock)
begin
if rising_edge(clock) then
cb2_c <= To_X01(cb2_i);
if rising = '1' then
if shift_active='0' then
if shift_mode_control = "000" then
shift_clock <= To_X01(cb1_i);
else
shift_clock <= '1';
end if;
elsif shift_clk_sel = "11" then
shift_clock <= To_X01(cb1_i);
elsif shift_pulse = '1' then
shift_clock <= not shift_clock;
end if;
shift_clock_d <= shift_clock;
end if;
if falling = '1' then
shift_timer_tick <= timer_b_tick;
end if;
if reset = '1' then
shift_clock <= '1';
shift_clock_d <= '1';
end if;
end if;
end process;
cb1_t_int <= '0' when shift_clk_sel="11" else serport_en;
cb1_o_int <= shift_clock_d;
ser_cb2_o <= shift_reg(7);
serport_en <= shift_dir or shift_clk_sel(1) or shift_clk_sel(0);
trigger_serial <= '1' when (ren='1' or wen='1') and addr=x"A" else '0';
shift_tick_r <= not shift_clock_d and shift_clock;
shift_tick_f <= shift_clock_d and not shift_clock;
process(clock)
begin
if rising_edge(clock) then
if reset = '1' then
shift_reg <= X"FF";
elsif falling = '1' then
if wen = '1' and addr = X"A" then
shift_reg <= data_in;
elsif shift_dir='1' and shift_tick_f = '1' then -- output
shift_reg <= shift_reg(6 downto 0) & shift_reg(7);
elsif shift_dir='0' and shift_tick_r = '1' then -- input
shift_reg <= shift_reg(6 downto 0) & cb2_c;
end if;
end if;
end if;
end process;
-- tell people that we're ready!
serial_event <= shift_tick_r and not shift_active and rising and serport_en;
process(clock)
begin
if rising_edge(clock) then
if falling = '1' then
if shift_active = '0' and shift_mode_control /= "000" then
if trigger_serial = '1' then
bit_cnt <= 7;
shift_active <= '1';
end if;
else -- we're active
if shift_clk_sel = "00" then
shift_active <= shift_dir; -- when '1' we're active, but for mode 000 we go inactive.
elsif shift_pulse = '1' and shift_clock = '1' then
if bit_cnt = 0 then
shift_active <= '0';
else
bit_cnt <= bit_cnt - 1;
end if;
end if;
end if;
end if;
if reset='1' then
shift_active <= '0';
bit_cnt <= 0;
end if;
end if;
end process;
end block ser;
end Gideon;