Last visit was: Thu May 01, 2025 2:40 pm
|
It is currently Thu May 01, 2025 2:40 pm
|
Attempting to build a Z80 SBC (many images)
Author |
Message |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
I've mostly been building 6502-based SBCs so I have some prior experience with 6502. Recently, I decided to try building one with Z80 for fun (CMOS TMPZ84C00, found one for ~$1 locally). I went through Z80 datasheet and some resources on the internet. Here's what I came up with. Design considerations: - $0000-$7FFF - ROM - $8000-$FFFF - RAM - 8 LEDs for visual output - Using perfboard for this, so trying to keep it small & simple - will be adding more fancy things later (LCD, UART, etc). - Simple 8-bit output through '273 flip-flop. - No input or interrupts for now. - I used AS6C4008 in schematic since it's included in Kicad parts by default. I have various SRAM chips available. Same with W27C512 EPROM. This is my first time doing anything with Z80, so please let me know if something seems terribly off. Any feedback is greatly appreciated!
You do not have the required permissions to view the files attached to this post.
Last edited by and3rson on Wed Jul 19, 2023 12:13 pm, edited 1 time in total.
|
Mon Jul 17, 2023 12:28 pm |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
I think I found a minor issue that would bite me once I add interrupts. I forgot that /IORQ goes low when an IRQ is being handled. This means accidental writes will happen to my '273 during interrupts. Seems like the easiest way to handle this is to gate /IORQ with /M1 (since /M1 is asserted during IRQ vector access) by introducing /IOEN, so that it's asserted when /IORQ is low AND /M1 is high. Then I can safely use /IOEN instead of /IORQ for my I/O:
You do not have the required permissions to view the files attached to this post.
|
Tue Jul 18, 2023 10:14 am |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
After some considerations, I've decided to add simple input through 8-section DIP switch & '244 tri-state buffer. I also replaced my 10-gate NAND address decoding with a single ATF16V8: it leaves more space on my PCB and allows me to reconfigure address decoding logic after I have my PCB printed, since I'm pretty sure there are some oversights in my design and I'll likely need to change something there. PLD will make it much easier.
You do not have the required permissions to view the files attached to this post.
|
Wed Jul 19, 2023 12:07 pm |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
Ha, I totally forgot to tie unused RAM & ROM address lines low. Having fixed that, I feel like this is the optimal version. Will be ordering a PCB tomorrow, hopefully I'll have it ready for testing by weekend!
You do not have the required permissions to view the files attached to this post.
|
Wed Jul 19, 2023 8:02 pm |
|
 |
BigEd
Joined: Wed Jan 09, 2013 6:54 pm Posts: 1821
|
looks nice!
|
Thu Jul 20, 2023 7:13 am |
|
 |
robfinch
Joined: Sat Feb 02, 2013 9:40 am Posts: 2307 Location: Canada
|
Looks great. I suggest if there is a next version to provide some pins with the address, data, and control signals present. It would make it easier to expand the I/O.
_________________Robert Finch http://www.finitron.ca
|
Tue Jul 25, 2023 2:31 am |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
robfinch wrote: Looks great. I suggest if there is a next version to provide some pins with the address, data, and control signals present. It would make it easier to expand the I/O. Absolutely. I'm planning to use Z84C20 PIO or even 65C22 VIA in next build. The purpose of this board was to see it I can handle Z80 and to get a very basic board working with least mistakes - i. e. to make sure there are no oversights with address decoding and clock, and possibly with more stuff I haven't thought about. I used a very simple program for my initial smoke test to see if CPU works at all and if my address decoding does handle RAM/ROM/IO properly. Code: org 0
; reset reset: jp main
; irq block 0x38-$ irq: reti
; nmi block 0x66-$ nmi: reti
; main main: in a, (0x00) ; Read from DIP switches out (0x01), a ; Write to LEDs jp main
Luckily, this build seems to work just fine! I'm surprised to have it at the first try. I guess my 6502 "experience" helped me somewhat. 
You do not have the required permissions to view the files attached to this post.
|
Tue Jul 25, 2023 10:17 am |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
Turns out I actually do have an issue: RAM doesn't seem to behave. It seems like data is simply not written to RAM. Furthermore, reading from RAM makes D0 read random values: the LED is constantly blinking. It feels like data bus is floating during RAM reads. Attachment: v1_ram_issue1.jpg I've seen somewhere that Z80 has very weak address lines. But the rest of the chips work just fine: some of my programs that use ROM & IO ('244, & '273), without RAM - work fine Attachment: bus_excerpt.jpg I'm wondering if the issue could be with /RD line not strong enough to enable RAM most of the time, and thus I could get floating data. All my chip-enabling lines are driven with ATF16V8B, I've used it in combo with AS6C4008 in the past without any issues. The lines that go from my Z80 to RAM are data bus, address bus, /RD, and /WD. Only RAM/EN (/CE) is controlled by ATF16V8. Do I need some buffers on address bus? Is Z80 not strong enough to control my RAM? I wish I had a good oscilloscope... Edit: found this thread - https://www.retrobrewcomputers.org/foru ... &#msg_6676, and as per their response seems like the safest way to go is indeed to have 3x '244 (address bus + control) & 1 '245 (data bus). Edit 2: I'm also suspecting that the cheapo Z84C0020PEC chips that I've got might in fact be re-labeled NMOS versions... 
You do not have the required permissions to view the files attached to this post.
|
Tue Jul 25, 2023 8:33 pm |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
Here's what I think should work for me: Attachment: v2_buffers.jpg - Data bus goes through '245, output during non-read cycles and input during read cycles (using /RD instead of /WR because Z80 drives data bus during T2 without asserting /WR). - Address bus goes through '244, constantly enabled (I'm OK with having my Z80 constantly controlling the address bus - I don't plan to do any DMA or bus sharing). - /RD and /WR are buffered through another '244, with additional version of /WR for chips that might require a non-zero bus hold time. AS6C4008 has a requirement for data hold time of 0, but I want to be on the safe side. I initially thought about 74LS14, but it's much slower than '244. - I'm ignoring /BUSACK, /WAIT, & /HALT since I don't use them. Once again, DipTrace does a wonderful job auto-routing all my mess: Unfortunately, too little space is left for PIO. Will have to figure something out.
You do not have the required permissions to view the files attached to this post.
|
Tue Jul 25, 2023 10:03 pm |
|
 |
BigEd
Joined: Wed Jan 09, 2013 6:54 pm Posts: 1821
|
Hmm, a difficult one to debug - not easy even to see whether the write is bad, or the readback.
But I feel it unlikely that the problem is a lack of drive, or a need for buffers. It feels more likely to me to be a timing problem, or possibly a logic problem, such that the situation during a write (or a read) isn't quite what you think it is. (Could even be a noise problem, perhaps.)
Can you successfully write and read back to a peripheral chip?
Can you share the datasheet of your RAM?
|
Wed Jul 26, 2023 11:59 am |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
BigEd wrote: Hmm, a difficult one to debug - not easy even to see whether the write is bad, or the readback.
But I feel it unlikely that the problem is a lack of drive, or a need for buffers. It feels more likely to me to be a timing problem, or possibly a logic problem, such that the situation during a write (or a read) isn't quite what you think it is. (Could even be a noise problem, perhaps.)
Can you successfully write and read back to a peripheral chip?
Can you share the datasheet of your RAM? Yeah, it's hard to probe my data lines because they seem to be heavily impacted by analyzer's capacitance. I've tested with AS6C1008-55PCN - https://eu.mouser.com/datasheet/2/12/AS ... 511508.pdf - not 4008 as I previously thought. I/O seems to work just fine: '244 correctly reads DIP switches when reading port 0, and '245 correctly latches all writes to port 1. Memory decoding with ATF16V8B also seems to yield correct chip selects, tested with a digital analyzer, and also wrote a simple test for my TL866 II Plus: Code: # addr.pld
GAL16V8 Addr
CLK /MREQ A0 A15 /IORQ /M1 NC NC NC GND /OE /ROM /RAM /IO /IN /OUT /LCDEN NC NC VCC
; ROM = $0000..$7FFF ROM = /A15 * MREQ ; RAM = $8000..$FFFF, avoid refresh cycles RAM = A15 * MREQ * /M1
; Qualify I/O with /M1 to avoid accidental I/O writes during interrupts IO = IORQ * /M1
; Port 0 - input IN = /A0 * IO ; Port 1 - output OUT = A0 * IO
DESCRIPTION Address decoder for Leo80
Code: # addr.toml (converted to .lgc using jedec2hex.py tool): [[ics]] name = "Addr-AFT16V8B" pins = 20 vcc = 5.0 vectors = [ "1XXXXXX00G0XXXXXXXXV",
# /MREQ, /IORQ, /M1 all high "110011X00G0HHHHHXXXV",
# /ROM "100011X00G0LHHHHXXXV", # /RAM "100111X00G0HLHHHXXXV",
# /IN "110001X00G0HHLLHXXXV", # /IN, but M1 is low "110000X00G0HHHHHXXXV", # /OUT "111001X00G0HHLHLXXXV", # /OUT, but M1 is low "111000X00G0HHHHHXXXV", ]
My code: Code: ; Reset/IRQ/NMI omitted for brevity
; Read 1-byte offset from 8 DIP switches and set LEDs to value from RAM on address (0x8000 + offset) loop: ; Read DIP switches in a, (0x00) xor a, 0xFF ; Flip values for convenience (my DIP switches are pulled up, so the value is inverted) ; Load A from 0x8000 + A ld l, a ld h, 0x80 ld a, (hl) ; Set LEDs out (0x01), a
; Wait ~0.6s ld hl, 0xFFFF ld a, 0 jp softim ; Cannot use call/ret due to RAM issues
; CPU sleep code from internet softim: inc a ;at least one pass ld b, a ;through outer loop ld a, 0 ;dummy instructions jp softm2 ;to kill time softm1: jr softm2 ;delay 16 T states softm2: nop softm3: dec hl ;decrement low order ld a,l or h ;hl = 0? jp nz, softm1 ;no, look again dec b ;b = zero? jr nz, softm3 ;no, repeat outer loop ; ret ;yes, return jp loop ; Cannot use call/ret due to RAM issues
Also, 0.1uF caps are placed near every ICs. ROM also works perfectly: it's just the RAM that's behaving strangely. I made a huge mistake not adding pin headers for bus & control lines: it would have made debugging much easier, especially for this first build. Data line "seems" to be stable during writes to RAM - at least I can see the proper bits on data line. I wonder if there's a simple way to differentiate whether it's a bad read, a bad write, or both: the only way I can think of is to halt the CPU and read from RAM using another device without powering it off. Maybe you're right and I'm missing some obvious timing violation in my chip selection scheme... I still am not used to understanding some obscure parameters in datasheet timing tables. :[ EDIT: I've noticed that it's only D0 and D7 that feel to be floating during RAM reads: D1-D6 always yield logical 1, no matter which RAM address I'm reading, but D0 & D7 are flickering when I touch them.EDIT 2: When adding 0.1s~0.5s busy loops, D0 blinks almost as if like it's toggled during every read. The blinking stops (becomes 0) when I touch it with my finger, tweezers, or attempt to probe it. However, this happens only when reading even addresses. EDIT 3: D0 blinking behavior seems almost unchanged when I remove my RAM whatsoever. EDIT 4: I tried 2 different 1008 RAMs, both reproduce all those issues.
|
Wed Jul 26, 2023 1:04 pm |
|
 |
Martin A
Joined: Thu Jul 27, 2023 8:21 am Posts: 2
|
It looks like you're qualifying your RAM access with M1, that's probably not a good idea.That's the CPU indicator that an opcode fetch is in process, it won't be active in a data read.
If you want to block refresh cycles from hitting memory then you need to have RFSH connected to the GAL. RFSH and M1 aren't mutually exclusive.
However with RD and WR connected to you RAM, there won't be any actual access, just a small increase in current as the RAM comes out of standby for the 2 refresh cycles when the chip enable is active.
|
Thu Jul 27, 2023 8:37 am |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
Martin A wrote: It looks like you're qualifying your RAM access with M1, that's probably not a good idea.That's the CPU indicator that an opcode fetch is in process, it won't be active in a data read.
If you want to block refresh cycles from hitting memory then you need to have RFSH connected to the GAL. RFSH and M1 aren't mutually exclusive.
However with RD and WR connected to you RAM, there won't be any actual access, just a small increase in current as the RAM comes out of standby for the 2 refresh cycles when the chip enable is active. Thanks - it totally flew over my head that I actually should not disable RAM when /M1 is asserted if I want to run code from RAM. Another update: I found the problem... I'm using AS6C1008, but my schematic is AS6C4008. For compatibility with 1008, I used to pull A15-A18 high in the past. However, in this build, I tied them low... While forgetting that in 1008, pin 30 is actually CE2 (active high)! So I literally hard-wired RAM to be always off... I'll break the trace to pin 30, tie it high and see if it does the trick.  Will post shortly. EDIT: Yep, that was it. 
Last edited by and3rson on Thu Jul 27, 2023 11:14 am, edited 1 time in total.
|
Thu Jul 27, 2023 10:36 am |
|
 |
Martin A
Joined: Thu Jul 27, 2023 8:21 am Posts: 2
|
If you want to check your CPU really is CMOS, there's a way to do it in code. Opcode ED 71 is officially undocumented. It's where OUT (C),(HL) would have been if it existed. On an NMOS CPU the effect is OUT (C),0. On the CMOS CPU it's effect is OUT (C),FF. Source: https://groups.google.com/g/comp.os.cpm/c/HfSTFpaIkuU/m/KotvMWu3bZoJSo if you load BC with the port address of the LEDs and follow that by the illegal opcode, you'll know from the state of the LEDs which type of CPU it is.
|
Thu Jul 27, 2023 11:08 am |
|
 |
and3rson
Joined: Sun May 28, 2023 12:11 am Posts: 10 Location: Lviv, Ukraine
|
Martin A wrote: If you want to check your CPU really is CMOS, there's a way to do it in code. Opcode ED 71 is officially undocumented. It's where OUT (C),(HL) would have been if it existed. On an NMOS CPU the effect is OUT (C),0. On the CMOS CPU it's effect is OUT (C),FF. Source: https://groups.google.com/g/comp.os.cpm/c/HfSTFpaIkuU/m/KotvMWu3bZoJSo if you load BC with the port address of the LEDs and follow that by the illegal opcode, you'll know from the state of the LEDs which type of CPU it is. Interesting! I just tested and it wrote 0xFF, so my CPU is indeed CMOS. Thanks for a neat trick!
|
Thu Jul 27, 2023 11:18 am |
|
Who is online |
Users browsing this forum: claudebot and 0 guests |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum
|
|