Designing Mega Drive/Genesis development cart, M68000 interfacing advice please? - Page 1
Forum section: electronics / projects

author: MrD
author_rank: Contributor
timestamp: October 25, 2019, 10:46:01 am
content: Hello, I'm designing a basic flash-style cart to read games and demos from an SD card and write them to flash memory, as an exercise. I'd like to getTitan's OVERDRIVE demoworking as I've met some of the makers. :)I'm somewhat new to hardware engineering so I'd like a critical eye over my hardware and software design before I try to make it for real. Any advice would be appreciated, especially if you're familiar with the 68000 and the Mega Drive/Genesis.(You might like myOcelot Arcade System,PSCD32 controller adapter, mylittle Master System gameor myYM2612 FM instrument editor.)My hardware stuff up until now has been pretty basic through-hole stuff. I was thinking of getting the PCB and SMT assembly done by JLCPCB so I'm constrained by the choice of parts they have. Luckily, the ones they have seem to lend themselves to a good layout.Background:The Mega Drive (aka Genesis) is a game console using a 68000 as its cpu. The console has a 64-pin cartridge port on the top for inserting game ROM carts. Most of the 68000 signals are exposed on this port. The MD expects to find a flat game rom mapped into $000000 up to $3FFFFF here, although since all the signals are exposed, the cart can respond to anything. I've made a custom PCB that uses a pair of 28C256s to make a 512kbit cartridge, but now I'd like to make something bigger. I could use SRAM but it's expensive, but Flash is cheap and it'll let me combine the mostly-non-volatile bootstrap rom I'd need beside a big blank area to write the game rom image into.My cart will have a 64Mbit Flash (8 MByte, 16-bit wide, 22-bit address space A0-A21) which I'll consider as two 4MByte halves. The lower 4MByte will be the BOOT half and will be selected when the FLASH A21 is low, this will contain the menu software and so on. The upper half will be a flat 4MByte address space to write a cartridge ROM image into.Here's my organisation for the cart: click to enlargeNot shown is the 5V->3.3V regulator, a tank cap for the board, or the caps for each IC or the !CART grounded detection pin.It'll boot up into a menu, read a list of files from the SD card, ask the user to pick one, then write the image to the upper 4MByte, then jump to it.A basic summary of the steps:1) !VRES is asserted, then negated. The cart starts connected to the 68000 address and data buses. The MCU sets up a rising edge interrupt on DETECTREGISTERCLK, then idles. The default state of !BOOT/GAME is to bankswitch in the lower 4MByte of the Flash. This will contain my menu code, etc. (I'm also going to use a 32Kbyte block of it to store the list of game names read from the SD card later.)2) The Mega Drive performs the normal boot/TMSS sequence reading from the lower 4MByte of the Flash, and then boots into the set-up sequence of my cart's firmware.3) The intro graphic is displayed, and a small code stub is copied to the Mega Drive RAM.4) The stub is jumped to - the stub writes $0000 to the !TIME address $A13000, then NOPs for 0.5s, then continually reads $000006.w and loops continually while the LSB is set.5) The write to the !TIME address $A13000 is decoded by the cart and both populates the content of the 16-bit register and triggers the MCU interrupt. The MCU NOPs briefly before acting.6) The MCU raises MCU!68000 high, which isolates the cartridge from the 68000 bus by tristating all the transceivers in both directions. Since D0 is pulled up by the cart, any word reads to cart space will have their LSB set (other bits indeterminate). Since I know that the lowest bit of $000007 will be zero (since this is the entry point vector in the header, it must be a multiple of two), the 68000 stub will continually read 1 in the LSB of $000006.w while isolated and 0 when the cart is non-isolated.7) While isolated, the MCU reads game filenames, titles and sizes from the SD card (or whatever data it needs) and uses FLASH!WE, the cart address and data buses to write the retrieved data to a 32 Kbyte block within the lower 4MByte of the Flash. (While isolated from the 68000 bus, the Flash chip's !C_CE will be pulled low.)8) When finished, the MCU sets up another rising edge interrupt on DETECTREGISTERCLK and lowers MCU!68000 to reconnect the cart to the 68000 buses.9) The 68000 can now exit the busy loop.10) The Mega Drive jumps back into the lower 4Mbyte of the cart Flash to run a menu program, with all the available games data available within that 32Kbyte block.11) The user selects a game - this is encoded as a 16-bit value (the index of the game in the order retrieved from the directory listing, or some other numeric identifier).12) A second stub is jumped to in RAM, and the selected game value is written to the !TIME address $A13000, then the Mega Drive NOPs for 0.5s, then continually reads $000006.w and loops continually while the LSB is set as before.13) The write to the !TIME address $A13000 is decoded by the cart and both populates the content of the 16-bit register and triggers the MCU interrupt. The MCU NOPs briefly before acting.14) The MCU raises MCU!68000 high again to isolate the cart.15) The value written to the register is retrieved into the MCU using REGISTER!OE and the cart's data bus.16) The MCU raises !BOOT/GAME to switch in the upper 4MByte of the Flash.17) The MCU can now open the indicated game file from the SD card and write it to the upper 4MByte of Flash.18) When done, the MCU can lower MCU!68000 to reconnect the cart to the 68000 buses. The MCU enters a low-power idle state and takes no further action. All of its pins are inputs except !BOOT/GAME which is high.19) The 68000 is released from its busy loop when the cart buses are reconnected.20) The Mega Drive cleans up any RAM/VRAM it needs to using its stub (in case certain software cares?), zeroes out itself as much as it can, then jumps to ($000004).l to boot the game present in the upper 4MByte of the Flash. From this point on, the cart is completely dumb ROM. Writes to !TIME will populate the register, but it will never be read.Does that sound like a good approach?--I'd like a second opinion on my glue logic and transceiver usage since I've never done this before.My signal names are (usually) prefixed by their source (68000 bus or MCU).I'm using transceivers to split the 5V 68000 world from the 3.3V internal world. The two 16-bit transceivers on the left should be considered as one 32-bit transceiver for the 'address and signals bus'. I'm using the !C_CE, !AS, R/!W, !TIME and !VRES signals from the cartridge slot to detect chip enables, reads, writes, strobes and reset conditions. If you're not familiar with the Mega Drive's signalling, my understanding is that !C_CE is asserted on reads or writes to $000000 - $3FFFFF and !TIME is asserted on reads or writes to $A13000-$A130FF.I'm using the 16-bit register because the MCU is way too slow to react to the 68000 quickly writing a word to the !TIME address space. With the register, I can store the value and use the rising edge of CLK to awaken the MCU, then it can isolate the cart and read the written register value at its own pace.Reset logic:FLASH!RST = 68000!VRESMCU!RESET = 68000!VRESThe PICkit is also connected to this pin when I'm programming the MCU, but you should never program the PIC while the cart is within the MD anyway. I'm going to have it so that you apply an external 5V source to the cart through header pins.Address decoding logic:FLASH!CE = 68000!C_CE (pulled low on isolate)The Flash should be selected on access to $000000-$3FFFFF, or always if the cart is isolated.FLASH!OE_EXTERNAL = 68000!C_CE + 68000!AS + NOT(68000R/!W)This signal represents an !OE request from a 68000 request. This happens when !C_CE is low, !AS is low and R/!W is high. I shouldn't need !C_CE strictly, but I'm using this combination as an input below.FLASH!OE = MCUFLASH!FORCEOE * FLASH!OE_EXTERNALI want the Flash to output a value if the MCU is forcing it to, or if the 68000 signals ask it to.REGISTER!WE = 68000!TIME + 68000!AS + 68000R/!WI'm going to have the register respond to any write in the $A130xx range, since that's simple. The register writes on the ascending edge of this signal, so it's also listed as REGISTERCLK.MCUDETECTREGISTERCLK = REGISTERCLKOn the rising edge of the register clock, the MCU awakes at the same time as the register is written.Transceiver logic:74ALVC164245 logic:A bus = 3.3VB bus = 5.0VDIR L = 3.3V output <- 5V input (value into cart from 68000 - DATA: a write operation, ADDRESS: 68000 Ax enters cart address bus.)DIR H = 3.3V input -> 5V output (value from cart into 68000 - DATA: a read operation, ADDRESS: cart address bus leaves onto 68000 Ax, do not allow this.)ADDRESSTRANSCEIVERDIR = lowOnly valid direction is an address from the 5V port placed on 3.3V port if ADDRESSTRANSCEIVER!OE is low.ADDRESSTRANSCEIVER!OE = MCU!68000When MCU is in !RESET or if this pin has not been raised, the 68000 address appears on the 3.3V address bus.DATATRANSCEIVERDIR = R/!WI'm surprised I don't have to invert this, the 68000's R/!W signals match the direction of the 74ALVC164245.DATATRANSCEIVER!OE = MCU!68000 + (FLASH!OE_EXTERNAL * REGISTER!WE)I want to connect the data bus if isolation is not in effect and we're valid reading the Flash or valid writing the register. (Notice my FLASH!OE requires FLASH!CE instead of being independent of it to allow the combination to be used here.)When the !OEs are low, the address or data buses will be floating, so I'm going to connect each pin to ground through 6k8 to prevent any unusual behaviour during isolation.I'm also pulling up !AS, R/!W, !TIME and !VRES high whenever the cart is isolated, to disable the Flash and register ICs unless the MCU activates them itself.MCU Outputs:While the MCU is in the reset state, all its pins are tristated. My pull- resistors need to make this a good safe state for the cart to be in before the menu begin, and while the real game is running and the MCU is idle:MCU!BOOT/GAME, low default, if low select the boot menu half of Flash, if high select the game half of Flash.MCUFLASH!WE, high default, allows the MCU to program the FlashMCU!68000, low default, if high the cartridge transceivers tristate the cart from the 68000 buses.MCUFLASH!FORCEOE, high default, if low the MCU is reading the Flash (to verify a write)MCUREGISTER!OE, high default, if low the MCU is reading the last value written to a !TIME addressMCU Inputs:DETECTREGISTERCLK, a rising edge wakes the cart for either the 'ready to read games' state or the 'please play game REGISTER' state.!RESET, standard reset.The Flash indicates a busy state by toggling a Qx pin on consecutive reads during busy, so there's no busy pin to poll.I haven't yet fully specified the SD card interface yet. I think I'm going to use the 4-wire SPI mode, with all the lines pulled to 3.3V through 3k3, and a 10R resistor on the +3.3V line to prevent high inrush current.In summary:1) Does my physical organisation make sense?2) Does the sequence of events make sense, and is it safe?3) Does my glue logic make sense?4) What is the best way (cheapest/lowest parts/fastest) to implement it?Please let me know what you think.Datasheets:TransceiverFlashRegisterPIC MCUMega Drive cartridge edge pinout.Regards,Matt C. :)Just learned that the 68000 R/!W signal isn't present on the cartridge port, and there's !UDW and !LDW upper and lower data write strobes instead. So is it the case that a read is taking place if neither !UDW and !LDW are asserted, and a write is taking place otherwise?So in my design I could addDERIVED R/!W = !UDW * !LDWand the rest of the semantics would be the same? I'd have to transceiver these and pull them up, etc.

author: bson
author_rank: Unknown
timestamp: October 26, 2019, 06:59:55 pm
content: I wouldn't recommend using the S39VF6401B, if you read the datasheet it specifies on p. 6:"The Program operation is accomplished in three steps.The first step is the three-byte load sequence for Software Data Protection. The second step is to load word address and word data."The problem is the SDP requires a specific address and data pattern that requires three bus cycles. If you're banging this it means updating all your 21 address lines - four times; three times for the SDP, and once for the data write. If you bit bang the control signals on top, believe me, writes will be incredibly slow. (Also, any firmware problem in the SDP will manifest itself as a never-completing write sequence.) I built an EPROM emulator around the SST39LV040 (which is 28C040 signal compatible) and didn't realize until too late that the device lacked a means to completely disable the protection. It destroyed all my planned code optimization around being able to write aligned 256-byte chunks in a tightly optimized loop that only had to update the low 8 address bits each time through. The SDP required me to update the ENTIRE address bus FOUR TIMES for each byte. Performance is pathetic, and while it worked okay for my intended purpose of trying out different firmware versions on an instrument, the poor write performance means it needs a complete redesign using some other flash chip. I can't imagine a cartridge that takes several minutes to program, that's just not realistic for a dev cycle. Just a heads up that there might be better options, like FRAM (not sure how large you need, they tend to be limited in size which is why I didn't use it).

author: MrD
author_rank: Contributor
timestamp: October 26, 2019, 11:46:04 pm
content: Thanks for the response. I did some calculations when I chose the Flash (though really I chose it because it was the only 64MBit part in the catalogue, and it was $3.50), and I never arrived at a value in the order of minutes for a full cartridge write. I always assumed that the SD card actions would be the bottleneck here.The size of the game area has to be 4 megabyte - that's the full address space for normal Mega Drive software. Games larger than this use game- and cartridge-specific bankswitching I'm not going to support. All software for the Megadrive of 4 megabyte or below expects to live in a flat x16 bit rom at $000000 - $3FFFFF.My original plan was to use SRAM, but I couldn't find cheap 4 megabyte SRAM (as a x16 part or two x8 parts) in JLCPCB's parts list. I could have made it from smaller parts, but then I'd need 3-to-8 decoder as well and things would get more complicated.The cartridge needs some non-volatile storage to boot into before any PIC manipulations take place anyway - using a single Flash was an attempt to satisfy the need for a non-volatile boot area and the need for a 4 megabyte storage space with a single part.Here's some back of the envelope timing calculations:In the datasheet for the Flash chip, I'm looking at the 'WE# CONTROLLED PROGRAM CYCLE TIMING DIAGRAM', the only non-zero relevant values are the width of the upper and lower halves of the #WE cycle (30 and 40 ns) and the word programming time after completing the sequence(10 us), which gives a minimum programming time for a single word of LHLHLHLHD = 4*(30ns+40ns) + 10 us = 10.28 us. To program 2*1024*1024 words has a strict minimum time of 20.97 seconds, which isn't so bad for the largest possible game size. Most games and demos don't fill the 4mbyte space (the demo I'm interested in running does though).I'd be happy with anywhere under a minute for this copy operation. I can fill the time with some cool transitions or animations or effects.I'll be running the PIC at 32 Mhz (16 MIPS), so 62.5 ns per instruction. The Port Output Rise/Fall Time have a max of 25 ns. I believe that this means that any single instruction will meet the #WE timing cycle requirements? (That is consecutive BCLR and BSET will result in a 62.5ns low pulse.) Since the PIC instructions are so fast and the word programming time is so slow, the writing sequence is surely going to be constrained by the Flash word programming time rather than bookkeeping and GPIO?My address and data buses are connected directly to the PICs gpio ports in the correct pin order. I've spread address pins A0-A7 over RE0-RE7 and A8-A20 over RB3-RB15, and data pins D0-D11 over RD0-RD11 and D12-D15 over C12-C15. The missing bit positions are unimplemented pins in this PIC. Since the data pins are arranged this way I can set the data bus by moving the 16-bit data value to both LATx registers without any swizzling. Addresses are always constant ascending so I can easily increment the two halves of my address counter. This means I should be able to perform the deprotect sequence in single instructions, since it's all constants.1) BSET !68000 high to isolate bus2) MOV Wconst, (A0-A7)3) MOV Wconst, (A8-A20)4) MOV Wconst, (D0-D11) (only need to set lowest eight bits to satisfy sequence)5) BCLR #WE low to begin low phase6) BSET #WE high to begin high phaserepeat 2-6 three times with all the constants required by the programming sequence stored in the register file.17) MOV f_Alow,Wn18) MOV Wn,19) MOV f_Ahigh,Wn20) MOV Wn,21) MOV f_data,Wn22) MOV Wn,23) MOV Wn,24) BCLR #WE low to begin low phase25) BSET #WE high to begin high phase26) NOP (when #WE set high the programming begins)27... busy wait on reading DQ6, wait until consecutive identical reads.- repeat for all words to write.(Edit - I wrote out the sequence in C, wondering if the compiler on -O2 would make a similar output.Yes I think it did, though be aware that the disassembly shows the statements out of order - the constants are assigned out of the loop.)So say it takes between 35 and 50 PIC instructions to write a single word, that's 50 / 16MHz = 3.125 us. Taking the datasheet's max word programming time of 10us, we're at 13.125 us per word. For 2*1024*1024 words, that's 27.525 seconds.A 32Kbyte block erase takes 18ms. To erase 4Mbyte takes 2.25 seconds. (The PIC time is negligible.)That's still below 30 seconds for the biggest possible image.I can live with that for a minimal chip solution.As an example, the ROM image of the Mega Drive port ofL'Abbaye Des Mortsis pay-what-you-want, and is listed as 3 Megabyte, which would take 22.5 seconds to load. As someone who was brought up playing a Spectrum +2A, that's more than fine. (In fact I'm tempted to make the Mega Drive imitate the yellow and cyan bars and loading screech now hehe.)Please let me know if you think any of my maths or reasoning is out.

author: MrD
author_rank: Contributor
timestamp: October 26, 2019, 11:50:17 pm
content: (Hmm, this max value for the erase is higher than the one quoted on the front page of the datasheet, but it doesn't change my answer much)

author: bson
author_rank: Unknown
timestamp: October 29, 2019, 06:20:23 pm
content: Good luck.

author: MrD
author_rank: Contributor
timestamp: January 10, 2020, 01:38:22 am
content: Hey again.I did change the Flash module in the end, so thanks for that advice. I didn't fully grasp what you were getting at at the time - I switched to the Cypress S29GL064S because it was cheaper, but coincidentally it had a fast 256-byte writing buffer mode, and a couple of other differences that made it go acceptably fast.Everything worked exactly as I'd hoped, in terms of the design using the !TIME register, the PIC, the Flash, the SD card, etc. All that communication worked exactly as I described it. (Which was one heck of a relief!)I've written abig ol' technical page about Game Raccoon Revision 0.Unfortunately, the Mega Drive motherboard -doesn't- follow normal 68000 signalling. There's !CE and !OE convenience signals provided by the motherboard, but I made my own using the 68000 !AS signal... but the VDP graphics chip in the Mega Drive doesn't emit this! So the normal flow of software works fine as the 68000 reads and executes instructions and handles blocks of data in ROM, but hardware accelerated copies from ROM to VRAM don't work - they just come out solid colour as my transceiver activation logic block doesn't see !AS and so tristates the data bus on the cartridge instead of outputting.As a result most graphical demos look rather strange...A more blog-like post with lots of sketches and wip images will come later.Any hints for Revision 1 would be gratefully accepted.