Interfacing a DCJ11 with a W65C22 VIA
DCJ11 and 6502 Bus
Now here is the challenge, the DCJ11 has an asynchronous bus interface and the 6502, und thus all 65xx peripherals, hava a synchronous bus interface. The W65C22 requires a steady PHI2 clock and all bus signals must be synchronized to this clock.
The good thing about the DCJ11 is that you can delay any read or write cycle to IO addresses as long as required.
Here is a picture of the test setup with the W65C22S on a breadboard connected
via the breadboard interface adapter to the DCJ11 SBC. On the top left you can
see the sole purpose of the test setup the blinkenlight
. But of course later
with the real deal on the Multi IO card you can do more.
PHI2
When synchronizing two busses it is very critical to avoid any glitches. Glitches can occur if the clocks of two systems are free running. This is also known as Clock Domain Crossing. However before we go into a design where the interface logic becomes more complex than the rest we should think if we can base both sides on the same clock with identical phase. The reason is that in Clock Domain Crossing situations you need to still obey all setup and hold times of one side with respect of the clock on the other side. This requires double latching and logic that is immune against meta states of flip-flops and latches. If the clock of the two sides have a common clock and phase then the setup and hold times are related to the clock and therefore known and deterministic.
The easiest way is to derive the clock of one side directly from the clock of the other side.
Clocking the DCJ11 with PHI2 does not make sense, even so the W65C22 would support a clock frequency for PHI2 of up to 14MHz. But when you are clocking the W65C22 with 14MHz then the timing will be very tight. Therefore I decided to derive PHI2 directly from CLK2 of the DCJ11 divided by two.
Frequency and Phase
The CPU cycles of a DCJ11 use always an even number of clock cycles and we always know when a DCJ11 CPU cycles start, we can use either ALE or STRB as the start, hence we can always tell which clock cycle of the DCJ11 CPU cycle is T0. For this reason I chose PHI2 to be CLK2 divided by two and the phase of PHI2 will be synchronized to T0 using either of the above mentioned signals.
Naming of Φ2
Officially the 65xx bus clock is named Φ2, however as I use a state machine in WinCUPL using the field [PH3..0] the signal that corresponds to Φ2 is actually named PHI0 in the PLD.
The following shows the three signals CLK (output CLK2 of the DCJ11), PHI0 (aka Φ2) and STRB in relation to each other
Of course the phase of Φ2 is delayed one transmission delay of the flip-flop with respect to CLK2, however the phase is always the same and we just need to take into account the switching delay of the flip-flop if we have to meet setup and hold time values.
The diagram shows a signal VIA which is typically just the chip select generated by an address decoder and is typically active during a CPU cycle of the DCJ11. This signal can be used to select the W65C22S via the CS2 input and then the VIAACT signal can be connected to CS1. With the combined use of the two signals the W65C22S is selected only if both are asserted, which is only in cycles that address the W65C22S and only during the Φ2 phases numbered 4 and 5.
Reading
Normally IO reads are stretched CPU cycles. However the DCJ11 reads the data first in the non-stretched part of the CPU cycle with the leading edge of T3. If data is not ready in the non-stretched part, the DCJ11 will again read the data in the stretched part using the falling edge of DV.
The address from the DCJ11 is ready at the falling edge of T0. In other words they
are ready well before the Φ2 clock marked with read
.
When we use the stretched part of the CPU cycle we then need to take care of DV to have the DCJ11 latch the date from the W65C22 when it is ready at the end of the active phase of Φ2.
Writing
Write cycles are always stretched. SCTL is asserted with the leading edge of T5 if CONT or leading edge of second T4 otherwise.
At the same time SCTL is asserted PHI2 will be low and we can use this PHI2 cycle to write the data. DAL are then guaranteed to have valid data.
Writing is also no problem, we just use SCTL and make sure we assert CONT immediately when the VIA is accessed.
Generating Φ2
Basically Φ2 is just CLK2 divided by two. However we need to track the cycles of Φ2 as we have multiple Φ2 clocks for one DCJ11 CPU cycle, in fact we have at last four Φ2 cycles in one CPU cycle as all CPU cycles that access the VIA are stretched and at least eight clock cycles. As we connected CONT to SCTL the CPU cycle takes indeed ten clock cycles and it would take even more if we delay CONT even more.
Therefore we do not just use a single flip-flop but a state machine with ten states, where the last two states are repeated until the DCJ11 finishes the CPU cycle.
SEQUNCE PHI {
PRESENT 0 IF STRB THEN 0;
IF !STRB THEN 1;
PRESENT 1 IF STRB THEN 0;
IF !STRB THEN 2;
PRESENT 2 IF STRB THEN 0;
IF !STRB THEN 3;
PRESENT 3 IF STRB THEN 0;
IF !STRB THEN 4 OUT VIAACT;
PRESENT 4 IF STRB THEN 0;
IF !STRB THEN 5 OUT VIAACT;
PRESENT 5 IF STRB THEN 0;
IF !STRB THEN 6;
PRESENT 6 IF STRB THEN 0;
IF !STRB THEN 7;
PRESENT 7 IF STRB THEN 0;
IF !STRB THEN 8;
PRESENT 8 IF STRB THEN 0;
IF !STRB THEN 9;
PRESENT 9 IF STRB THEN 0;
IF !STRB THEN 8;
}
For each CLK2 we advance to the next state, except if SCTL is asserted. In this case we know a new DCJ11 CPU cycle will start and so we start our sequence with the next CLK2 with state 0.
We know that if the VIA is selected the states 4 and 5 of Φ2 exist and we will use these for the transfer. Writing is easy, we just need to combine the select VIA signal with the VIAACT signal generated by the state machine.
Reading is needs more attention as at the same time VIAACT is deasserted we also need to de-assert DV respecting the hold time of the DCJ11 (tDVDH in the data sheet says 35ns). When the VIA is the only device that requires DV to be tailored, then the logic for generating DV is simple. DV will be always asserted when the VIA is not selected and when the VIA is selected DV will only be asserted during PHI cycles 4 and 5.
Register Addresses
As the register select of W65C22S are connected to A1..4 the W65C22S registers occupy 16 words. Only the lower byte is valid. Users of the SBC-11/21 will be familiar as this is the way the ZIO is connected. The reason for this is that we do not have a write enable signal that is active for all bytes and therefore we just use the low-byte of words to address the W65C22S.
Blinkenlights
The following blinks a LED connected to any output of Port A of the VIA. First it sets all IO to output, then just counts down R0 to zero, sets the output of Port A high, counts down R0 again and then sets the output of Port A low and branches to the beginning of the blink loop. A DCJ11 clocked with 4MHz will blink approx. once a second. At 20MHz it blinks quite fast.
00000072/012737 ; MOV #377, @#174006 ; DDRA
00000074/000377
00000076/174006
00000100/077001 ; SOB R0, 100 ; Wait Loop
00000102/012737 ; MOV #377, @#174002 ; ORA
00000104/000377
00000106/174002
00000110/077001 ; SOB R0, 110 ;Wait Loop
00000112/012737 ; MOV #0, @#174002 ; ORA
00000114/000000
00000116/174002
00000120/000767 ; BR 100
00000122/000000
00000124/000762 ; BR 72
As you can see you need to double the address offset to address a register. E.g. DDRA which is register 3 requires to use the address offset 6 in other words can be set using address 1740068.
GAL22V10 Adresse decoder and W65C22S PHI2 state machine
The GAL used to support the W65C22S not only implements the state machine to generate Φ2 for the W65C22S but also includes an address decoder with four outputs. The two additional outputs ROM and SLU are later used for the Multi IO card. For the test setup only CON and VIA are required.
Name MULTIIO;
Partno GAL;
Date 24/04/2025;
Rev 02;
Designer cbscpe;
Company Netzwerk & Design GmbH;
Assembly None;
Location None;
Device G22V10;
/*
This is the glue-logic used for the simple multi-IO card. The multi-IO
card comes with a second serial interface using again a DC319, a VAI
W65C22S and an EEPROM which can hold Boot ROM.
The multi-IO card is an expansion card for the DCJ11 SBC.
Before using this card you need to disconnect !IO from the Glue-Logic of
the DCJ11 SBC. This is either the GAL16V8 (U18) or the 74HCT139 (U14).
In either case it was recommended that you use sockets for these ICs and
thus you can just bend the !IO pin of the IC so that it does not make
contact when the IC is again inserted into the socket. This is because
the glue-logic of the multi-IO expansion card now provides the !IO signal
to select the on-board UART, which is no longer selected for the complete
IO-page.
W65C22S Interface Glue-Logic:
The W65C22N VIA has a synchronous bus and therfore we need to sychronize
access of the DCJ11 to PHI2 of the W65C22S.
NOTE: Typically the signal for the clock on the 65xx bus systems is
called PHI2. In this design PHI0 is actually the bus clock to be used
for the 65xx bus, this is due to the way WinCUPL uses arrays and how
GALs are assembled which will not allow renaming clocked outputs.
This GAL implements a state machine which counts the DCJ11 clock cycles.
The LSB of the state machine is used as clock of the W65C22. Clock half
cycles are numbered 0, 1, 2, .. to 9. Where PHI0 is low for even and
high for odd half cycles. !STRB is used to synchronise the PHI cycles to
the DCJ11 clock cycles. I.e. PHI cycle 0 always follows clock cycle T0
of the DCJ11.
During the second and third T4 of of the stretched DCJ11 CPU cycle the
65xx bus and the DCJ11 are in sync, therefore the W65C22S may be selected
during PHI0 clock phases 4 and 5, during which VIAACT is asserted.
The falling edge of DV can be used to latch data into the DCJ11 before
the DCJ11 CPU Cycle ends. If DV is asserted during the whole DCJ11 CPU
cycle data is latched at the end of T5, but then the VIA is already no
longer selected. Therefore we de-assert DV when the IO-Page range for
the is selected and assert it only during VIAACT is asserted, creating
a falling edge at the end of the 65xx bus cycle that accesses the VIA.
In all other cases we use the normal behaviour of the DCJ11 SBC and assert
DV during the complete cycles. Because if this logic inversion of normal
DV behaviour the DV output might use more product terms, make sure that
DV output is using one of the OLMC with many PT.
Note that the VIA registers are addressed using A1, A2, A3 and A4 and
WEL is used as the RW signal of the 65xx bus. Therefore you need to
double the address offset to access a given register.
Addressing
Note that we create the following addressing scheme in the IO-page
of the DCJ11
177xxx is used for the console
176xxx is used for the second serial interface matching the standard
address for the first DLV11, 17650x
174xxx is used for the W65C22S VIA
173xxx BootROM
165xxx BootROM
Compile
.\cupl.bat Z:\DCJ11SBC\DCJ11SBC-W65C22S.PLD
Program
minipro -p GAL22V10 -w DCJ11SBC-W65C22S.jed
*/
PIN 1 = CLK;
PIN 2 = !STRB;
PIN 3 = A9;
PIN 4 = A10;
PIN 5 = A11;
PIN 6 = A12;
PIN 7 = LAIO1;
PIN 8 = LAIO2;
PIN 9 = LAIO3;
PIN 10 = LBS0;
PIN 11 = LBS1;
PIN 13 = LAIO0;
PIN 14 = PHI0;
PIN 15 = PHI1;
PIN 16 = PHI2;
PIN 17 = PHI3;
PIN 18 = VIAACT;
PIN 19 = DV;
PIN 20 = !SLU;
PIN 21 = !ROM;
PIN 22 = !CON;
PIN 23 = !VIA;
FIELD PHI = [PHI3..0];
PHI0.ar = 'b'0;
PHI1.ar = 'b'0;
PHI2.ar = 'b'0;
PHI3.ar = 'b'0;
VIAACT.ar = 'b'0;
PHI0.sp = 'b'0;
PHI1.sp = 'b'0;
PHI2.sp = 'b'0;
PHI3.sp = 'b'0;
VIAACT.sp = 'b'0;
SEQUENCE PHI {
PRESENT 0 IF !STRB NEXT 0;
IF STRB NEXT 1;
PRESENT 1 IF !STRB NEXT 0;
IF STRB NEXT 2;
PRESENT 2 IF !STRB NEXT 0;
IF STRB NEXT 3;
PRESENT 3 IF !STRB NEXT 0;
IF STRB NEXT 4 OUT VIAACT;
PRESENT 4 IF !STRB NEXT 0;
IF STRB NEXT 5 OUT VIAACT;
PRESENT 5 IF !STRB NEXT 0;
IF STRB NEXT 6;
PRESENT 6 IF !STRB NEXT 0;
IF STRB NEXT 7;
PRESENT 7 IF !STRB NEXT 0;
IF STRB NEXT 8;
PRESENT 8 IF !STRB NEXT 0;
IF STRB NEXT 9;
PRESENT 9 IF !STRB NEXT 0;
IF STRB NEXT 8;
}
DV = !(LBS1 & !LBS0 & A12 & A11 & !A10 & !A9)
# (LBS1 & !LBS0 & A12 & A11 & !A10 & !A9) & VIAACT;
VIA = LBS1 & !LBS0 & A12 & A11 & !A10 & !A9 & VIAACT;
CON = LBS1 & !LBS0 & A12 & A11 & A10 & A9;
SLU = LBS1 & !LBS0 & A12 & A11 & A10 & !A9;
ROM = LBS1 & !LBS0 & A12 & !A11 & A10 & A9
# LBS1 & !LBS0 & !A12 & A11 & !A10 & A9;