With the PCB’s in and the initial hardware check finished and me delaying far too long on getting back to this project, it was time to move on to verifying that my intended hardware functionality did, in fact, work as planned. There are several functional blocks that need to be tested, both on the AVR and the Z80 support circuitry.
For this post, I’ll focus on three specific functional blocks:
- AVR as the Z80 system clock
- AVR interface with the DS3231 RTC module
- AVR interface with the system, data and address buses
On past projects, especially when acquiring and evaluating various OEM samples to be used with a microcontroller, I’ve always found it time consuming to write different test programs over and over again. Always having to configure, reconfigure, flash, test, debug, fix code, re-flash and do it all over again. Those real-time debug cycles on any FLASH-based MCU can be very time consuming, which I sought out ways to cut the time. To that end, I ran across Dick Cappels‘ AttoBASIC back in 2011. With some modifications to the code, I was able to fully support the TWI and SPI interfaces for the project I was working on at the time. Since then, I have found it to be indispensable in evaluating new hardware devices and initial hardware testing when an AVR is the embedded microcontroller on a particular project, such as this Z80 SBC. It also helps to be the AttoBASIC maintainer (since 2011) as I know the program well enough to add support for any feature if I find I have a need for it. For the following initial hardware testing, I have been using AttoBASIC and to provide others with examples, I will provide each program listing that was used for the testing.
With all the I/O ports that the ATMEL AT90USB1286 provides, I still found myself a little bit shy in ports. I wanted to leave the JTAG interface and some A/D channels available, thus PORTF was not fully accessible. I ended up using a 74HC299 to interface to the Z80 databus. The 74HC299 is an “8-bit universal shift register with 3-state outputs“. Its a nice part that comes in a 20-pin package. I chose to use the DIP package for this project. From the 74HC299 datasheet:
The 74HC299; 74HCT299 contain eight edge-triggered D-type flip-flops and the interstage logic necessary to perform synchronous shift-right, shift-left, parallel load and hold operations. An operation is determined by the mode select inputs S0 and S1, as shown in Table 3. Pins I/O0 to I/O7 are flip-flop 3-state buffer outputs which allow them to operate as data inputs in parallel load mode. The serial outputs Q0 and Q7 are used for expansion in serial shifting of longer words.
1st test, Z80 clock: I had already tested this function on my breadboard using an assembly language program but that was with an ATmega328P. It should work the same on the AT90USB1286 but I wanted to be sure … and it did. Its a simple program; initialize all ports to their proper state, setup TIMER 2 as the Z80 clock source and generate a pulse on the Z80 CLK pin. The majority of the program listing is for documentation. Here’s the AttoBASIC program listing:
5 REM Z80 POWER-UP INIT 10 REM PD2=IORQ, PD3=MREQ, PD4=WR, PD5=RD 15 REM PF1/PF0 = S1/S0 FOR WS-GEN 20 REM PB0=HC299_CS, PB6=HC299_OE, PB7=HC299_S1 25 REM PB4=Z80CLK, PE2=Z80RST, PD6=Z80BUSRQ 30 OPA $FF, OPC $FF # SET ADDRESS PORT AS INPUTS W/ PUPS (C=A[15:8], A=A[7:0]) 35 OPB $D0; ODB $D0 # SET PORTS 40 OPD $FF; ODD 0 # SET PORTS 45 OPE $F3; ODE $04 # SET PORTS 50 OPF $00; ODF $03 # SET PORTS 55 REM SETUP TMR2: TCCR2A(@$B0)=$42, TCCR2B(@$B1)=$01, OCR2A($B3)=$07 60 X:= PEEK 0 $64; X:= X AND $BF; POKE X 0 $64 # TMR2 ENABLE IN PRR0 65 POKE $3F 0 $B3; POKE $42 0 $B0; POKE $01 0 $B1 # SETUP TMR2 AS 125KHZ Z80 CLK 70 PBE2 # GENERATE A RESET PULSE TO CYCLE REGISTERS 75 END
2nd test, RAM R/W test: To test the address, data and system control buses, I initially wrote a program to set the AVR’s I/O ports, then with the Z80 removed from the circuit, I wrote data to the RAM, read the data back from the RAM, incremented the address bus and repeated the cycle for all 65,536 bytes of RAM. I then decided to expand upon that by testing the RAM with the Z80 in-circuit. After all, that will be the final goal, to use the AVR as a host controller with the Z80 in-circuit. Below is the logic analyzer’s capture of the AttoBASIC program that is testing RAM. What is important to note here is that in testing the memory, I am also testing the AT16V8 address decoder.
I only have 16 channels available on my logic analyzer so I chose to capture the Z80’s DATA[7:0], ADDR[2:0], RD, WR, MREQ, BUSREQ and BUSACK, triggering on the falling edge of the BUSACK signal. In doing so, I could test that the Z80 responds to a request for the bus, relinquishes the bus and acknowledges it. During the test, the Z80’s clock was active and a reset pulse was applied before dropping the BUSREQ line and waiting for the BUSACK line to drop active low. Once BUSACK was detected low, the program proceeded to sequentially test each RAM location. The address can be seen incrementing and although the scale is too large to see the contents of the data bus, a 0x55 is being written and read back then complimented so that a 0xAA is written and read back from the next location. The state of the MREQ, RD and WR lines can also be seen.
RAM Test w/ AttoBASIC
I would not say that AttoBASIC was speedy when it came to testing, as sequential memory accesses took about 15.88 mS, which was about 63 bytes per second. As calculated, it would take 17 minutes and 19.4 seconds to complete the test if there were no errors to report. In actuality, the AttoBASIC program reported that it took 17 minutes and 9.3 seconds to complete and there were no errors. However slow it may be compared to pure assembly or “C” code, it served it’s purpose. The AttoBASIC program code is listed below:
5 REM Z80 RAM ERROR TEST 10 REM PD2=IORQ, PD3=MREQ, PD4=WR, PD5=RD 15 REM PB4=Z80CLK, PE2=Z80RST, PD6=Z80BUSRQ 20 REM PF1/PF0 = S1/S0 FOR WS-GEN 25 REM PB0=HC299_CS, PB6=HC299_OE, PB7=HC299_S1 30 SPM 2# SPI TO MODE 2 35 D:= $55; Z:=0 # DATA TO WRITE=$55, Z HOLDS '0' 40 GOSUB 220 # INSURE ALL PORTS AS INPUTS 45 GOSUB 225 # START Z80 CLOCK AND INITIATE A Z80 RST 50 SDD6; CBD6 # INITIATE Z80 BUSRQ 55 IF 1 = IBE0 # WAIT FOR Z80 BUSACK 60 GOTO 55 # LOOP TILL Z80 BUSACK 65 ODA $FF; OPA $00 # SET PORTA OUTPUT AND ADDRESS A[7:0] =0x00 70 ODC $FF; OPC $00 # SET PORTC OUTPUT AND ADDRESS A[15:8] =0x00 75 OPB '11010001; ODB '11010111 # SS=1 (SEL HC299), OE=1 (HI-Z), (S0=1/S1=1) 80 ODD '11111100; OPD '10111111 # CONTROL PORT TO OUTPUTS, ALL HIGH 85 ODE '00000100; OPE '11111111 # SET PORTE, INPUTS W/ PULL-UPS 90 ODF '00000011; OPF '11111100 # SET S0/S1 OUTPUTS FOR 0 WS 95 RTI 2; RTR # SET 100MS RTC INTERVAL AND RESET THE RTC 100 FOR U=0 255; FOR L=0 255 # SET UP THE UPPER AND LOWER ADDRESS LOOPS 105 OPC U; OPA L # OUTPUT CURRENT ADDRESS ONTO BUS 110 GOSUB 165 # WRITE DATA TO THE RAM ADDRESS 115 GOSUB 180 # READ DATA FROM THE RAM ADDRESS AND PRINT IT 120 SBD5; SBD3 # RAISE THE Z80 RD AND MREQ SIGNAL 125 IF X != D # RESULT = EXECTED? 130 GOSUB 195 # ERROR, INFORM USER 135 D:= COM D # COMPLIMENT DATA TO WRITE (ODD/EVEN ADDR) 140 NEXT L; NEXT U # CONTINUE INCREMENT LOOP 145 PRI PEEK VPG@RTC1; PRI PEEK VPG@RTC0 # PRINT THE CONTENTS OF THE RTC (IN 0.1 SEC) 150 ODA $FF; ODB $FF; ODC $FF; ODD $FF # SET ALL PORTS TO INPUTS 155 END 160# *** WRITE DATA TO DATA BUS *** 165 CBB7; SPW D; CBB6; CBD3 # SHIFT ENABLE, WRITE DATA='D', ENABLE DATA ONTO # BUS, DROP Z80 MREQ 170 CBD4; SBD4; SBD3; SBB6; SBB7; RET # PULSE Z80 WR, RAISE Z80 MREQ, OE=1,S1=1 THEN RETURN 175# *** READ DATA FROM DATA BUS *** 180 CBD3; CBD5; SPW 0; SBD5; SBD3 # DROP Z80 MREQ, DROP Z80 RD, DUMMY WRITE TO # LOAD, RAISE Z80 RD, RAISE Z80 MREQ 185 CBB7; X:= SPR; SBB7; RET # S1=0, 'X'=READ DATA, S1=1 THEN RETURN 190# *** PRINT ERROR MESSAGE *** ; 195 PRI "ERROR DETECTED AT:~" # INFORM USER OF ERROR IN TESTING 200 PRI "A[15:8]="; PRX U # INFORM USER OF UPPER ADDRESS 205 PRI "A[ 7:0]="; PRX L # INFORM USER OF LOWER ADDRESS 210 PRI "EXPECTED= "; PRX D; PRI "RESULT = "; PRX X 215 PRINT "~"; RET # INSERT CR/LF BETWEEN ADDRESSES 220 ODA Z; ODB Z; ODC Z; ODD Z; ODE Z; ODF Z; RET # ALL PORTS TO INPUTS 222 REM SETUP TMR2: TCCR2A(@$B0)=$42, TCCR2B(@$B1)=$01, OCR2A($B3)=$07 225 X:= PEEK 0 $64; X:= X AND $BF; POKE X 0 $64 # TMR2 ENABLE IN PRR0 230 SDB4; POKE 63 0 $B3; POKE $42 0 $B0; POKE 1 0 $B1 # SETUP TMR2 AS 1MHZ Z80 CLK 235 SDE2; SBE2; PBE2; RET # GENERATE A RESET PULSE TO CYCLE REGISTERS AND RETURN
DS3231 RTC test: The AVR is directly connected to the DS3231 RTC via the I2C (TWI) bus. I had already written a program to interface with a TEXAS INSTRUMENTS bq32000, which is also a real-time clock with a nearly identical register set as the DS3231. It took only a slight modification to that program and I was able to test the DS3231. The program sets the date and time to a set value, waits 4 second, reads it back again and displays the result. The AttoBASIC program code is listed below:
5 REM DS3231 RTC TEST 10 TWI 1 # INIT TWI @100kbps 15 A:= $D0 ; B:= $D1 # WRITE AND READ ADDRESSES FOR BQ32000 RTC 20# *** CONFIGURE THE DS3231 25 TWS ; TWW A $E 0; TWP # write config register 30# *** INITIALIZE TIME AND DATE DATA TO RTC REGISTERS 35 DATA $00 $00 $60 $00 $22 $12 $16 # setup RTC's time and date 40 FOR Z=0 6 # set data registers 0 to 6 45 R:=Z ; D:= READ # set register and data 50 TWS; TWW A R D; TWP # Write data to a register 55 NEXT # end of loop 60# *** WAIT A BIT FOR THE SECONDS TO CHANGE THEN READ THE REGISTERS BACK 65 PRINT "Wait 4 sec~" ; SLP 8 # wait 4 seconds to see if the clock is running 70 TWS; TWW A 0 # Select register 0, no STOP 75 TWS; TWW B; TWR 7; TWP # slave to read and retrieve 7 bytes 80# *** LOOP AND PRINT EACH REGISTER'S VALUE 85 FOR R=0 6 # Loop from register 0 to 6 90 PRINT "R:" ; PRI R # print the register 95 PRINT "D=" ; PRX READ # print the data in the register 100 NEXT # end of loop 105 END
I did not test the wait-state generator as it was previously tested on the breadboard circuit, thus I already know it works.
An additional note: the overall current consumption is measured at ≈250 ma.
This completes the testing of the major functional blocks on the Z80 SBC. The next phase is to start writing the AVR host controller firmware.
Peace and blessings.