Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 19 of 19

Thread: Generating clock signal for an 8 bits 6502 CPU

  1. #1
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68

    Generating clock signal for an 8 bits 6502 CPU

    I've read several threads on the forum about generating clock signals, timer but I'm puzzled I don't see how to achieve what I want

    I'm trying to use a T3.6 as a "glue" to an NMOS 6502 CPU - At this stage 28 meaningful signals from the 6502 are wired to Teensy pins and I triple checked the connections, and even arranged pins usage to be grouped by port with the future hope to handling ports instead of handling pins

    I've spent countless hours trying to get some results without stable clock (main loop speed IS the clock) but I very often only get erratic behaviour from the CPU. Might be caused by a bunch of reasons : Me doing things the wrong way, wrong phase timing (clock HI, read address bus, clock LOW), or this NMOS 6502 could not really like being driven at 3.3v, or too much noise from the breadboard wiring forest...

    But before giving up, or trying with a 65C02 (CMOS version which is known to be happy under 3.3v, even if some source says that 3.3v could be ok with some NMOS series) I would like to try something : generate a stable clock signal from a timer, A 1MHZ one - Of course there's https://www.pjrc.com/teensy/td_pulse.html but that does not seems to fit my bill because I need to run code during the HI state - Then attachInterrupt and try to get pin change for some signals (R/W and PHI2)

    How can I do this ? I mean coding this fast interrupt timer... Is there a teensy lib that I've missed ? Or some example code somewhere ? Or I'm left with the K66P144M180SF5RMV2.pdf bible and dissecting the Teensy core lib ?

    EDIT : I wonder if I could use a 1 microsecond IntervalTimer, that would mean 1 000 000 calls per second, which is 1MHZ
    Last edited by Tactif CIE; 06-03-2019 at 04:47 PM.

  2. #2
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,415
    Make the choice of a pin which is associated to one of the Flextimers FTM0 to FTM3 (these are the PWM capable pins) as a clock pin. Make sure you do not need that FTM for PWM or input capture purposes, so that you might use it exclusively for clock generation. Now, do not use the "usual" high level Arduino PWM functions, but write the FTM MOD register directly by setting it to 59 if F_BUS is 60MHz (happens at F_CPU=180MHz) or to 47 if F_BUS is 48MHz (happens at F_CPU=192MHz) and the corresponding timer channel compare register (depends on your selected pin) to 30 or 24 (again depending on F_BUS) to get a highly stable 1MHz clock independent of interrupt latency and other constraints. Finalize your code following the example of the analogWrite() source code in the Teensyduino core files to start the timer.

  3. #3
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Ok thanks for the hints ! I'll give it a try and come back here if I can't find my way through low level programming ;-)

  4. #4
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    I made some progress, and managed to have a timer isr called around 1000000 times per second but I'm facing a weird crash that I don't understand

    I extracted all the meaningless parts of the code

    Code:
    #include <Arduino.h>
    
    volatile uint32_t count;
    uint32_t last_count;
    uint32_t last_time;
    
    const uint8_t addr_pins[16] = {32, 31, 30, 29, 1, 0, 17, 16, 5, 21, 20, 6, 8, 7, 14, 2};
    
    FASTRUN void ftm0_isr(void) {
      FTM0_SC &= ~FTM_SC_TOF;
      count++;
      for (unsigned int i = 0; i < 16; i++) {
        //digitalReadFast(addr_pins[i]);
      }
    }
    
    void setup() {
      count = 0;
      last_time = millis();
      while (!Serial)
        ;
    
      Serial.println("Entering setup");
      FTM0_SC = 0;
      FTM0_CNT = 0;
      FTM0_MOD = 59;
    
      for (int i = 0; i < 16; i++)
        pinMode(addr_pins[i], INPUT);
    
      NVIC_SET_PRIORITY(IRQ_FTM0, 32);
      NVIC_ENABLE_IRQ(IRQ_FTM0);
    
      FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0) | FTM_SC_TOIE;
      Serial.println("Leaving setup");
    }
    
    void loop() {
      uint32_t c;
      if (millis() - last_time >= 1000) {
        cli();
        c = count;
        sei();
        Serial.println(c - last_count);
        last_time = millis();
        last_count = c;
      }
    }
    The offending line of code is

    Code:
    digitalReadFast(addr_pins[i]);
    doing 16 digitalReadFast() calls with pin constants is fine but as soon as I use addr_pins[i] the Teensy freezes.
    I really don't understand what the problem is...

    The fault occurs the same way with TeensyDuino 1.45 or a custom Makefile with teensy core from github master

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    429
    Just an untested idea: can it be that reading the 16 lines with variable pin numbers simply takes too long, so that the interrupt will be called again immediately after it finishes? (It is much faster with constant pin numbers). Would be easy to test by measuring the time spent in the ISR for both versions (constant / variable pin numbers).

  6. #6
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Good catch luni ! I tried with fewer iterations and yes, until 4 it's ok, above TS freezes

    So I'll try to optimize the isr code the best I can with digitalReadFast with constants and later with direct ports access

    Thanks !

    EDIT : It's 4 iterations max with digitalRead and 6 with digitalReadFast

  7. #7
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Just tried with constants, at 1MHZ the max digitalReadFast calls I can do before freezing is 8/9 (with 2 digitalWriteFast)

    So I'm stuck and need to switch to ports reading / writing

  8. #8
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    429
    I did a quick measurement and got some 50ns for a constant DigitalWriteFast and some 80ns with variable pin number.
    Instead of using DigitalReadFast with variable pins from an array you can also precalculate the BitBandRegister addresses from the pin numbers and read from the registers directly. This gives the same performance as with the constant pin numbers but you can use variable pins.

    Code:
    ...
    // store the bitband PDIR (Port Data Input Register) adresses 
    volatile uint32_t *pin_bitband_pdir[] ={
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[32].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[31].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[30].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[29].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[1].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[0].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[17].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[16].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[5].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[21].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[20].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[6].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[8].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[7].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[14].reg + 16 * 32),
            (uint32_t *)((uint32_t)digital_pin_to_info_PGM[2].reg + 16 * 32),
    };
    
    
    
    FASTRUN void ftm0_isr(void)
    { 
      FTM0_SC &= ~FTM_SC_TOF;
      count++;
      for (unsigned int i = 0; i < 12; i++)
      {
        volatile uint32_t val = *pin_bitband_pdir[i];
      } 
    }
    ...

    However, this is still way too slow. So, as you already mentioned, reading a whole port should be much better.
    What exactly do you want to achieve?

  9. #9
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    It's an "educational" attempt, for the fun, to connect an old 6502 CPU to the teensy (the 6502 was a cpu found in countless computers from the mid 70's -> 80's)

    I'm trying to generate a clock and read back the address and data bus - The cycle, for what I understand from the 6502 specs, is made of several phases between the clock HI and the clock LOW
    I've tried from the main loop without interrupts and at some points got a correct behaviour after resetting the cpu but not for long, any modification of the loop() code produce clocking changes and nothing was stable - and the "clock" generated was way too slow compared to what I need

    So, I though about having a timer and doing one cpu cycle from there, then I could use volatile flags for different states that could be used to communicate to and from the main loop

    But that also mean that I need to breadboard the stuff in a better way - My wires are way too long and there must be a lot of noise (as you can see in the attached picture) - Need to buy a better breadboard (this one is pure crap), with custom made wires of proper length.

    But it's really fun, even if most of the time frustrating ;-) and a good opportunity to learn about digital signals

    Click image for larger version. 

Name:	IMG_20190603_222705.jpg 
Views:	9 
Size:	303.4 KB 
ID:	16725

  10. #10
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    BTW, Luni, how did you measured the execution time of the isr in nano seconds ?

  11. #11
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    429
    I added a DigitalWrite(3, HIGH) at the beginning of the ISR and a DigitalWrite(3, LOW) at the end and used a cheap LA to measure the duration of the resulting peak on pin 3 for various iteration numbers. Did a quick Excel plot of the time vs. iteration + regression line. The slope gives you the time per iteration. Sounds more complicated than it really is :-)

  12. #12
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Ho yeah ! A clever use of a Logic Analyzer again ;-) https://forum.pjrc.com/threads/56496...logic-analyzer

    I really learn a lot on this forum !

  13. #13
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Luni, yesterday I received a cheap Saleae LA - Seems to work fine and it helped me to find that my duty cycle was wrong so I fixed the problem until I got a more-or-less 50% duty cycle at 1 MHZ
    I've switched to reading and writing ports directly instead of using DigitalReadFast and DigitalWriteFast but I need to measure the execution time of a portion of code of my isr routine.

    Can you explain the method you used in post #8 (time vs iteration + regression line), that would help me a lot !

    I have both Seleae Logic and Sigrock available...

  14. #14
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    429
    Im currently on a vacation bicycle trip. Hard to explain on the mobile. Basically you record the time for different number of loops. Draw a loops Vs time graph in any spread sheet app or manually. Determine the slope of a linear regression curve or draw the line manually. Slope is then time/loop. Advantage is that this does not depend on constant timing.

  15. #15
    Junior Member
    Join Date
    Jun 2019
    Posts
    3
    Hi, if it can help I just got a Teensy to drive 65C02 (WDC CMOS Version) on a breadboard.
    I tested it up to 1.2Mhz and it’s stable even though the duty cycle is not 50/50. I don’t use any interrupt so far, and emulate RAM, ROM and Serial. Check if it works with your NMOS, it might help identify your problem.
    You can find it here: https://github.com/olivierjan

  16. #16
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Hi Olivier ! Glad to see that I'm not the only fool on earth to play with a Teensy and a 6502 :-)
    I adapted the Bus_access_simple example to my own wiring, but that did not worked. I got nothing, even lowering delay() in loop to small values

    Before giving a try with interrupts I did a lot of experiments without them, just with a loop() as you do. At some points, randomly and barely I got some results - I could see the 6502 at last trying to read the RST vector ! I could even write something to the data bus that was not reliably read by the 6502.

    But most of the time I got erratic behavior.

    So it's time to face the obvious : if this cpu is not counterfeit (which I think it's not, bought from an established resaler, no traces of upper side lifting or strange printing) this NMOS version can't run at 3.3v, period, and I have to buy a 65C02. I really wanted to try with an NMOS but I must admit the only solution would be using a Teensy 3.5 and a level shifter at the loss of some horse power, and the T3.5 is at the same price as the T3.6.

    The NMOS have also few cons :
    - triggering a RESET with a simple push button is not reliable while the CMOS version have a schmitt trigger thing on its RESET pin which prevents or help dealing with bouncing (AFAIK)
    - the NMOS version can't run under a frequency under 100KHZ, its internal state is not maintained, whilst you can single step the CMOS easily
    - the NMOS version is more peaky on duty cycle than the CMOS, but this is not a problem with interrupts because thanks to the FTM I managed to get a quasi 50% duty cycle

    Second reason of this failing attempt could be the shitty breadboard that I got from a 15$ Amazon kit - I'm very very suspicious, it's so bad quality that I could not even plug the Teensy on it without taking the risk to break the pins. I had to put the Teensy on a second breadboard which is better but too small for both the Teensy and the 6502.

    But the last reason is may be I'm doing something wrong...

    I wired the pins so :
    6502 Data bus D0..D7 is on C0..C7 on Teensy
    6502 Address bus A0..A7 is on D0..D7 on Teensy
    Then I didn't had any 8 consecutive bits free port (don't have access to back pins) and choosed to
    A8..A9 on B0..B1
    A10..A13 on B16..B19
    A14..A15 on B11..B12

    Bit shifting seems to be right, I triple checked the connections and the pins <-> port relation
    Then PHI2 OUT is on E24, RESET on E35, and R/W on C10

    Dunno but I'm about ordering a 65C02, just to investigate further while being more confident ;-)

  17. #17
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Just re reading my post I spotted than using C10 for R/W is wrong because C port is being put in Input or Output mode depending on the cpu cycle...

    So I switched R/W to E26, but still get erratic behavior...

  18. #18
    Junior Member
    Join Date
    Jun 2019
    Posts
    3
    Why did you connect PHI2 OUT? The clock signal from teensy arrives on PHI2 not PHIW OUT, which by the way is more or less deprecated on the latest 65c02.

  19. #19
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Sorry, that's a typo, the clock signal generated by the Teensy feed the pin 37 of the 6502...

    I meant PHI2 signal OUT (of the Teensy)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •