Close

AVR Clocking (overthinking)

A project log for Improbable AVR -> 8088 substitution for PC/XT

Probability this can work: 98%, working well: 50% A LOT of work, and utterly ridiculous.

eric-hertzEric Hertz 01/24/2017 at 21:440 Comments

A nice surprise, that I'll save for future-endeavors:

I figured OSCCAL would allow adjustment of the 8MHz (nominal) R/C oscillator with only a small variance in either direction, but here it seems I could adjust it to (roughly) Three Times the 8088 Bus Clock (14.31MHz)!

I've vague ideas of how to fine-tune that... maybe using an 8-bit timer/counter, and counting external 4.77MHz clocks until it overflows, counting internal clocks at the same time, and adjusting the OSCCAL value until we get ~768 internal clocks between timer-interrupts. That'd probably be accurate-enough... Especially if I have a routine that waits until an edge is detected before starting each bus-transaction.

It's entirely plausible it'll sync-up somewhere mid-clock, and that's something I'll be pondering. Maybe it means we can only, reliably, execute two timing-sensitive instructions per bus-clock... I dunno.

---------------

External-clocking at 4.77MHz (1:1 AVR/8088 clocking) also seems feasible (and would certainly be easiest for these early experiments):

Note, no mention of duty-cycle. (and, in fact, the drawing isn't even close to 50%!)

(because, the 8088's clock is 4.77MHz with 33% duty-cycle).

-------------

Finally: I think I've managed to whittle down the bus-transaction such that with 1:1 clocking, the AVR should be able to keep up within the requirements of external memories and I/O (and the 8288). (Though, NOT, the 8087 and DMA controller... which I'll leave uninitialized for now).

#define S20_INT_ACK     0x0
#define S20_READ_IO     0x1
#define S20_WRITE_IO    0x2
#define S20_HALT        0x3
#define S20_INSN_FETCH  0x4
#define S20_READ_MEM    0x5
#define S20_WRITE_MEM   0x6
#define S20_PASSIVE     0x7
#define S20_IDLE    S20_PASSIVE //my own naming...

void bus88_write(uint8_t s20, uint32_t address, uint8_t data)
{
   //////
   // 4.77MHz = 209.6ns
   //  clock-high = 33% duty-cycle = 69.88ns
   //  clock-low =  66%            = 139.76ns

   //////
   //AVR latches inputs on the falling-edge
   // 'in R17, PINA' is valid *half* a period after, or in the *next*
   // clock-cycle
   // (and R17 contains the value at the rising edge of the one following)
   //
   //AVR latches *outputs* on the rising-edge *after* the instruction
   // completes.

   //Note, all AVR-OUTPUTs, here,  are expected within ~20ns after bus's 
   //       Falling-edge
   //      all AVR-INPUTs, here, are expected within ~20ns after bus's
   //       Rising-edge

   // So, maybe a 7404, or something, could be found with ~20ns
   //  propagation-delay, and wired between the bus-clock and the AVR
   //  clock-input. 
   //  Or, chain a few inverters in series.
   //  (Believe it or not, this was once a go-to method for synchronizing
   //   digital logic... Sometimes you'll even find a production-board with
   //   a chain of inverters and a clock-signal hand-wired to a point
   //   somewhere inbetween)

 //### Bus is idle (assumed) ###

   //So, it should be OK to prep our Address bytes
   ADDR1916_PORT   = (uint8_t)(address>>16); //assuming bits 20-24 are 0
   ADDR158_PORT    = (uint8_t)(address>>8);
   ADDRDATA70_PORT = (uint8_t)(address);

   //Begin the Bus transaction
   //Per 8288:
   // This should happen between tCHSV after _|¯ and tSVCH before next
   // tCHSV = Status Active Hold Time >= 10ns after _|¯
   // tSVCH = Status Active Setup Time >= 35ns before next(?!) _|¯
   // So, basically, somewhere around T1's entry ¯|_
   S20_PORT       = s20;   // OUTPUT, AROUND: ¯|_ 

   // The 8288 will bring ALE High...
   //  Either from the clock ¯|_, or from S20 ¯|_, if after clock

 // ¯|_  ### T1 ###  
   // Normally, Addresses would be setup here,
   //  but we're fudging the timings a bit... 
   //  They've already been done.
   // The 8288 will bring ALE Low in 4-15ns after _|¯

   //Nothing to do?!
   // Gotta keep the timing aligned...
   asm("nop;");

 // ¯|_  ### T2 ###  
   // "T2 is used primarily for changing the direction of the bus during
   //  read operations" -- 8088-CPU.pdf
   // The 8288 will bring /AMWC, /AIOWC, etc. low in 10-35ns after ¯|_
   //  for "advanced" (early) write-signals
   //  (Not really important to note here)
   // FOR READ: This is the same time where /MRDC, or /IORC occurs
   //TODO: Should probably make sure the ALE pulse is recognized
   //  by other devices, before we change AD7:0
   //  BUT: it's already been half a clock-period, right?
   //  So should be fine...
   //  ALSO, if we meet/exceed the 8088's minimum timings, here, 
   //  then it should be fine.

   //Put the data-to-write on the port
   //8088: TCLAX ¯|_ -> A19:16 -> S6:3 Address Hold >= 10ns
   //      TCLAZ ¯|_ -> AD7:0 -> Float (READ) Address Float Delay 10-80ns
   //      TCLDV ¯|_ -> AD7:0 -> Data (WRITE) Address Valid Delay 10-110ns
   ADDRDATA70_PORT = data;  //OUTPUT: >10ns AFTER ¯|_


   //Technically: A19:16 -> S6:3 is also supposed to happen here
   // but for now we're not using the 8087, nor DMA, so should be OK(?)

 // ¯|_  ### T3 / Tw ###  
   // The 8288 will bring /MWC, /IOWC, etc. low in 10-35ns after ¯|_
   //  for *non*-advanced/non-early write-signals

   //It's not possible to read the READY signal, test it, then jump
   // all in a single AVR cycle
   //Thankfully, it seems the 8288 is triggered only by /S2:0
   // And the processor is the only thing that pays attention to READY
   // And, nothing else is expected of the processor outputs during T3/Tw
   //So, we can insert our own "wait-states"
   // in the form of internal processing-time, then return /S2:0 to "idle"
   // when it's complete

   //According to one diagram (showing READY being sampled!)
   // READY should be sampled with ¯|_ entering T3/Tw 
   // in 8088-CPU.pdf p10
   //According to another diagram, READY should be sampled with _|¯
   //!!! TRYLCL could be up to 8ns *into* T3 (but not Tw)
   // TRYHCH setup time > 118ns to _|¯ mid T3/Tw!!!
   // TCHRYX hold-time > 30ns after _|¯
   // in 8088-CPU.pdf p23
   //Confusing, but the actual *timings* suggest _|¯
   //
   // OK, sample READY on _|¯
   while(!(READY_PIN & READY_MASK))  {};  //INPUT: <30ns AFTER _|¯

   //NOTE That we'll be in a later Tw, at this point... weeee!
   //Indicate that we're done with wait-states, and ready to enter T4
   //8088: TCLSH Status Inactive Delay: after ¯|_ 10-130ns
   S20_PORT = S20_IDLE; //OUTPUT: >10ns AFTER ¯|_

 // ¯|_  ### T4 ###
   // The 8288 will bring /IOR, /IOW, etc. high within 10-35ns of ¯|_
   // We're not reading, so we don't need to sample ADDRDATA_PIN
   //  But, for READ, it should be done quickly after ¯|_, or maybe before
   // 8288 timing shows Write Data Valid as acceptably valid after T4
   //  Same likely goes for A19:8... (whew!)

   //Nothing to do?!
   asm("nop;");

 // ¯|_  ### BUS IDLE ###
   // WARNING: This is NOT compatible with DMA and 8087!
}

In case you didn't catch that, lemme take out the majority of the comments:

   ADDR1916_PORT   = (uint8_t)(address>>16); //assuming bits 20-24 are 0
   ADDR158_PORT    = (uint8_t)(address>>8);
   ADDRDATA70_PORT = (uint8_t)(address);

   S20_PORT       = s20;   // OUTPUT, AROUND: ¯|_ 
 
   asm("nop;");

   ADDRDATA70_PORT = data;  //OUTPUT: >10ns AFTER ¯|_

   while(!(READY_PIN & READY_MASK))  {};  //INPUT: <30ns AFTER _|¯

   S20_PORT = S20_IDLE; //OUTPUT: >10ns AFTER ¯|_

   asm("nop;");

SO MUCH SIMPLER than the earlier expectations, wherein I thought it would be darn-near impossible to get it running with a 4:1 AVR clock. And, here it should work with 1:1.

------------

I've still gotta wire this thing up, and the cat's being deprived, so it may be a while until I get a chance to check this out.

Thankfully, READ is pretty much identical, (and has been accounted-for in the comments)...

And it'd be pretty easy to e.g. use the AVR's UART (or just an LED) to indicate whether writes/reads to a low memory-address verify correctly. (A bit of a shout-out to comments in earlier logs, suggesting using the AVR's peripherals... I'd been planning to do some CGA video-output for my first "hello-world" but that's way overboard)

--------

And... the 8515 might just work with only a few pin-relocations, it's got a very similar pinout to the 8088.

Discussions