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.

VIA with Blinkenlight

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.

W65C22S Register

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;

Download

CUPL project including JEDEC file to program GAL22V10 used for the bus interface and the address decoder for 65xx peripherals. Note this is the version used in the Multi IO card described in the next chapter.