Extending the CTRL code space

Using, learning, programming and modding the Gigatron and anything related.
Forum rules
Be nice. No drama.
Post Reply
lb3361
Posts: 360
Joined: 17 Feb 2021, 23:07

Extending the CTRL code space

Post by lb3361 »

This post is a request for comments about a possible way to make small changes today in order to organize how various people can design expansion boards that do more and more useful things.

Background

The RAM & IO expansion boards are controlled by a native instruction named CTRL using a clever hack devised in Marcel's RAM & IO expansion thread (https://forum.gigatron.io/viewtopic.php?f=4&t=64). This instruction simply repurposes a nonsensical ST instruction that talks to the memory with both the write enable signal (/WE) and output enable signal (/OE) asserted. Without an expansion board, this instruction writes a more-or-less random byte at the specified address. The expansion board detects this, prevents the write-enable signal from reaching the RAM, and uses the sixteen bits of the address as a code to control some hardware.

In the last iteration of the original expansion board, these sixteen bits are used as follows:
  • A0 controls the SPI clock and whether the Gigatron reads from the RAM or from the MISO lines.
  • A1 is not connected and not usable because a smart optimization causes A1 to go up and down during SPI operations.
  • A2..5 control four SPI device selection lines
  • A6..7 control the RAM bank presented in the upper half of the address space (128KB = 32KB * 4)
  • A8..14 are not connected but are not usable because they go up and down during SPI operations.
  • A15 controls the SPI MOSI line.
My gal-based expansion board (https://forum.gigatron.io/viewtopic.php?p=2047#p2047) is compatible enough with the original one to work with the unmodified ROM. However it only offers two SPI ports because repurposes A4 and A5 for other features. It also slightly changes what A0 does:
  • A0 controls the SPI clock and whether reading at address 0x0000 reads from the RAM or from the MISO lines.
  • A4 controls the SPI clock polarity (in order to implement more SPI modes)
  • A5 controls zero page banking, that is, the ram bank presented at addresses 0x80-0xff.
Hans61's combined RAM, IO, and VideoRepeater board (https://forum.gigatron.io/viewtopic.php?p=2606#p2606) currently uses CTRL like the gal-based expansion board and uses a complex chip attached to SPI1 to control other features. However Hans61 seems eager to remove the complex chip and offer a collection of GPIOs using the CTRL instruction. Since other people might build even more cards in the future (Ethernet anybody?) it seems wise to devise a way to split what is left of the ctrl code space. One problem of course is that none of the sixteen bits seem usable and that several bits have already been overloaded (guilty here.)

Although vCPU programs cannot issue the native CTRL instruction, they can call a native routine named SYS_ExpanderControl_v4_40 with a code in the accumulator vAC. Interestingly this routine ensures that bits A0 and A1 are always zero because the expansion board would completely disable the RAM and make the Gigatron unusable. It also saves the low byte of the ctrl code at location 0x1f8 where other code can see which bank is active, which spi device is selected, etc.

First part of the proposal

The first challenge is to find usable code space. We can observe that in the current scheme, A2 and A3 should never be low together because that would simultaneously select two SPI devices and potentially cause shorts. So the proposition is to declare that ctrl codes with zeroes in both A2 and A3 are a different space, triggering none of the usual functions of the ctrl code bits, but triggering instead new functions.

Code: Select all

  Normal ctrl codes:     m------- bbssSS-c    where bits SS are 11, 10, or 01
     - functions: as explained above 
  Special ctrl codes:    xxxxxxxx pppp0000
     - pppp represents a hardware function code
     - xxxxxxxx control the hardware function pppp
Note that the low nibble of the special codes is 0000 because SYS_ExpanderControl does not let us change A0-A1 at will. The hardware function code pppp represents a new hardware function. For instance hardware functions 0000 and 0001 could each implement eight GPIO lines that the ctrl instruction sets to xxxxxxxx. We can allocate up to 16 hardware function codes like that.

The SYS_ExpanderControl routine should not store the low byte of special ctrl codes in address 0x1f8 because there are expectations about what the bits stored in 0x1f8 mean. Although new boards could recognize the special codes and make sure that they do not change the selected bank or the selected spi device when receiving a special code, existing boards cannot be expected to do the same. Therefore we also need to make sure that special control codes do not leave such boards in a bad state.

Both issues can be addressed by changing SYS_ExpanderControl as follows (just a proposal for discussion for now). This proposed routine first checks whether it was given a normal or special code. Bits A0 and A1 are cleared in both case. Normal codes are stored in 0x1f8 and issued with a ctrl instruction as usual. Special codes are not stored in 0x1f8. They are issued with a ctrl instruction, then are followed by a second ctrl instruction that reissues the code that was previously stored in 0x1f8. In other words, we make sure that cards that do not recognize the special codes are reset in the same state as before. They might briefly select two SPI devices at the same time, but I am hoping here that this will be harmless because we do not cycle the SPI clock during this time.

Code: Select all

label('SYS_ExpanderControl_v4_40')
ld(hi(sys_ExpanderControl),Y)       #15
jmp(Y,sys_ExpanderControl)          #16
ld(hi(ctrlBits),Y)                  #17
....
label('sys_ExpanderControl')   
ld(0xc)                             #18
anda([vAC])                         #19 check for special ctrl code space
beq('sysEx#22')                     #20
ld([vAC])                           #21 load low byte of ctrl code in delay slot
anda(0xfe)                          #22 sanitize normal ctrl code
st([Y,ctrlBits])                    #23 store in ctrlBits
st([vTmp])                          #24 store in vTmp
bra('sysEx#27')                     #25 jump to issuing normal ctrl code 
ld([vAC+1],Y)                       #26 load high byte of ctrl code in delay slot
label('sysEx#22')
anda(0xfe,X)                        #22 * special ctrl code
ld([Y,ctrlBits])                    #23 get previous normal code from ctrlBits
st([vTmp])                          #24 save it in vTmp
ld([vAC+1],Y)                       #25 load high byte of ctrl code
ctrl(Y,X)                           #26 issue special ctrl code
label('sysEx#27')
ld([vTmp])                          #27 load saved normal ctrl code
anda(0xfe,X)                        #28 sanitize ctrlBits
ctrl(Y,X)                           #29 issue normal ctrl code
ld([vTmp])                          #30 always load something after ctrl
ld(hi('REENTER'),Y)                 #31
jmp(Y,'REENTER')                    #32
ld(-36/2)                           #33
Second part of the proposal

Another problem is to make sure that additional functions of the expansion boards are reset properly when the Gigatron itself is reset. Here I am proposing to use another property of the normal codes. A0 and A1 can only be set by the ROM. This only happens in the SPI routine. And this routine only sets them to values 00, 01, and 10. In other words, we can use a normal code with both A0 and A1 set to indicate a reset. The very beginning of the ROM could be changed as follows.

Code: Select all

# Setup I/O and RAM expander
ctrl(0b01111111)                # Reset signal
ctrl(0b01111100)                # Disable SPI slaves, enable RAM, bank 1
#      ^^^^^^^^
#      |||||||`-- SCLK \   b01=11 => RESET
#      ||||||`--- n.c. /   
#      |||||`---- /SS0 \   b23=00 => SPECIAL CODE
#      ||||`----- /SS1 /  
#      |||`------ /SS2 or /SPOL
#      ||`------- /SS3 or /ZPBANK
#      |`-------- B0
#      `--------- B1
# bit15 --------- MOSI = 0
Then expansion board could reset its state to a safe default whenever receiving a ctrl code with A0 and A1 set to 11. Note that this works for a cold reset. What about a warm reset achieve by pressing start for 5 seconds or by jumping to 0x1f0 ? Do we need something like this in SYS_Reset?

Hardware

One of the good properties of this proposal is that the special code can be detected by simply ORing A2 and A3, and resets can be detected by ANDing A0 and A1. But this is not that simple because this comes in addition to making sure that we have a ctrl code by ORing /WE and /OE, etc. A bit more logic is needed.

On the negative side, I was hoping to be able to ensure the gal-based expansion board would safely ignore the special codes by merely reprogramming the GALs without changing the PCB. It turns out that this does not seem possible because of the way the two GALs split the work. On the other hand, I believe it is possible to split the work differently in order to make sure that the same GAL receives A2 and A3 as input and is in charge of deciding whether to treat the ctrl code as a normal code (see below).

Code: Select all

gal1
  INPUTS     CTRLCLK /OE /WE A0 A1 A2 A3 A4 MISO0 MISO1 /NA15TO5   (11)
  OREGS      /SS0 /SS1 SCK SCLK                                    (4)
  OWIRES     /OEPORT /OERAM /WERAM DPORT CTRLCLKOUT SCK0           (6)

  CTRLCLKOUT = OE & WE & (A2 # A3)
  SSO.D = !A2
  SS1.D = !A3
  SCLK.D = A0
  SCK.D = A0 $ !A4
  SCK0 = SCK & !SS1
  APORT = NA15TO5 & !A4 & !A3 & !A2 & !A1 & !A0 & SCLK
  OEPORT = OE & APORT
  OERAM = OE & !APORT
  WERAM = WE & !OE
  DPORT = SCLK & ((SSO & MISO0) # (SS1 & MISO1))
--- 
gal2
 INPUTS     CTRLCLK A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5        (12)
 OREGS      B0 B1 /ZPBANK MOSI                                    (4)
 OWIRES     A15RAM A16RAM /NA15TO5                                (3)

 NA14TO8 = !A14 & !A13 & !A12 & !A11 & !A10 & !A9 & !A8
 NA15TO5 = !A15 & NA14TO8 & !A7 & !A6 & !A5
 B0.D = A6
 B1.D = A7
 ZPBANK.D = !A5
 MOSI.D = A15
 BANKEN = BANKEN = A15 $ (ZPBANK & NA14T08 & A7)
 A15RAM = BANKENABLE & B0;
 A16RAM = BANKENABLE & B1;

Curiously this is a little bit more compact that the current organization. It leaves three usable I/O pins in GAL2 that would be best used to do address decoding work because GAL2 has most of the address lines as inputs. But that will not work without changing the schematics and designing a new PCB.

Conclusion

Comments and suggestions are welcome!
Last edited by lb3361 on 28 Oct 2021, 14:16, edited 1 time in total.
at67
Site Admin
Posts: 647
Joined: 14 May 2018, 08:29

Re: Extending the CTRL code space

Post by at67 »

Your theory looks great to me, I love the idea of general purpose IO like the old days, I wasn't a real fan of the SPI interface. Are you planning on keeping a registry of devices and device ID's?

P.S. I want an ID for a SPO256-AL2 speech synthesizer add on, (I've got a bunch of them handy waiting for Gigatron's).
lb3361
Posts: 360
Joined: 17 Feb 2021, 23:07

Re: Extending the CTRL code space

Post by lb3361 »

Comments on the design still very welcome!

Questions:
  • I convinced myself that the brief assertion of two /SSx lines will not cause problem because all RAM & IO expansion board designs I know about have some kind of short protection with separate MISO lines. What do you think?
  • I am less clear about what to do for cold and warm resets. When the Gigatron powers up, it performs a cold reset by executing the native code at address 0x0000. This codes makes low level intialization, constructs the warm reset trampoline at address 0x1f0, and points the vCPU to address 0x1f0. This trampoline calls SYS_Reset which initializes a few more things (video and sound) then calls SYS_Exec() to load and execute Reset.gt1. This Reset.gt1 program basically clears the screen, initializes the sound tables, check for the presence of an expansion board and possibly starts the SD card boot program, or simply starts the familiar MainMenu.gt1 program. When one presses the Start button for 3 seconds, the vCPU jumps to address 0x1f0, bypassing the initial cold reset procedure. I guess that the goal was to permit intercepting a warm reset by overwriting the code at address 0x1f0. I do not know if any program does this. Anyway, does resetting the expansion board belong to the cold reset (at power up only) or to the warm reset? I am inclined for the latter...
More ideas to discuss:
  • All the boards make sure that they do not simply connect the MISO lines of various SPI ports in order to prevent shorts. Instead they drive them separately into the board logic. That certainly works but does not allow for adding SPI ports by simply using GPIO lines as device selects. Wouldn't it be simpler to do the following (a wired or with pull-ups before and after) ?
    Screenshot from 2021-10-26 09-45-35.png
    Screenshot from 2021-10-26 09-45-35.png (23.31 KiB) Viewed 2657 times
  • One of the benefits of moving the MISO combination out of the logic chips is that it would free 3 additional pins of the GALs. Instead one could do some address decoding for GPIOs, for instance providing clock signals for function (the pppp bits) that could be used to latch A8-A15 into 74hct273 chips for instance, providing 8 convenient output ports per chip.
  • What I have been calling GPIO so far are in fact outputs only. If we need inputs, we can use the four upper bits that are sent to the bus when reading while SCLK is high. The four lower bits are used by the MISO lines already (and the firmware depends on all four). But another idea is to use the address bus to select more input bytes. For instance the gal-based board only provides the port data when one reads from address zero. One could get eight additional bits by reading from address 1, etc.
at67
Site Admin
Posts: 647
Joined: 14 May 2018, 08:29

Re: Extending the CTRL code space

Post by at67 »

lb3361 wrote: 26 Oct 2021, 14:01 Questions:
  • I convinced myself that the brief assertion of two /SSx lines will not cause problem because all RAM & IO expansion board designs I know about have some kind of short protection with separate MISO lines. What do you think?
IMHO when designing hardware you should never rely on someone else's input protection/glitch supression solving a bug in your hardware, that's really bad design. Not only are you relying on a third party feature for correct operation, but you are relying on each vendor's implementation of that feature. If that feature is part of a spec that is guaranteed, then you have more leeway, but it is still poor design to produce outputs that could potentially electrically short or cause a timing conflict and rely on someone else to solve the problem.

In your case you may have no other option, but I personally would explore every possible avenue to make all outputs glitch free electrically and with respect to timing, (especially electrically!).
lb3361 wrote: 26 Oct 2021, 14:01 [*]I guess that the goal was to permit intercepting a warm reset by overwriting the code at address 0x1f0. I do not know if any program does this. Anyway, does resetting the expansion board belong to the cold reset (at power up only) or to the warm reset? I am inclined for the latter...
[/list]
Warm reset...Marcel intentionally coded his reset routines the way he did so that the firmware, (native code), would not need to change for each new piece of hardware that was introduced, (just add your initialisation code to the GCL reset routine, i.e. warm reset). Changing the early initialisation native code can be much more problematic then just adding a some extra vCPU code to the GCL reset, (even though you still need to burn a new ROM for both).
lb3361 wrote: 26 Oct 2021, 14:01
  • All the boards make sure that they do not simply connect the MISO lines of various SPI ports in order to prevent shorts. Instead they drive them separately into the board logic. That certainly works but does not allow for adding SPI ports by simply using GPIO lines as device selects. Wouldn't it be simpler to do the following (a wired or with pull-ups before and after) ?
    Screenshot from 2021-10-26 09-45-35.png
I'm confused are the MISOn outputs open collector/open drain or push pull? If they are open then you can tie them all together with one pull-up resistor and no diodes, (check the data sheets!), if they are push-pull then you need the steering diodes and once again only one pull up or pull down resistor, (the direction of the diodes and the choice of pull up or pull down depend on whether you are implementing a wired OR, or a wired AND).
lb3361 wrote: 26 Oct 2021, 14:01 [*] One of the benefits of moving the MISO combination out of the logic chips is that it would free 3 additional pins of the GALs. Instead one could do some address decoding for GPIOs, for instance providing clock signals for function (the pppp bits) that could be used to latch A8-A15 into 74hct273 chips for instance, providing 8 convenient output ports per chip.
More old school PIO and less modern SIO is what I would like to see personally.
lb3361 wrote: 26 Oct 2021, 14:01 [*] What I have been calling GPIO so far are in fact outputs only. If we need inputs, we can use the four upper bits that are sent to the bus when reading while SCLK is high. The four lower bits are used by the MISO lines already (and the firmware depends on all four). But another idea is to use the address bus to select more input bytes. For instance the gal-based board only provides the port data when one reads from address zero. One could get eight additional bits by reading from address 1, etc.
[/list]
PIO inputs would be great, I'd include some basic input protection from over voltage/surges/spikes and possibly noise, (so clamping diodes, current limiting resistor and possibly an RF bead, though the RF protection is probably overkill).
lb3361
Posts: 360
Joined: 17 Feb 2021, 23:07

Re: Extending the CTRL code space

Post by lb3361 »

Thank you for your comments. Scrutiny is essential.
at67 wrote: 26 Oct 2021, 18:28 IMHO when designing hardware you should never rely on someone else's input protection/glitch suppression solving a bug in your hardware,..
I could not agree more. The idea here is to specify how future expansion boards should behave when receiving a ctrl code with both bits 2 and 3 set to zero. Using a compliant expansion board is the normal operation of the proposed scheme. Under those conditions, there are no problems. The concern is only about what happens when somebody runs a program that uses special ctrl codes on a Gigatron equipped with an expansion card that predates this spec. This is something that cannot work and that people should not do.

In the current state of affairs, if you insert the wrong board and start doing bizarre ctrl things, anything can happen. Oje of my goals is to improve on this. This is easy when the boards obey the convention. But since older boards cannot be expected to do so, I propose to program SYS_ExpanderControl in a manner that follows every special ctrl code by a normal ctrl code whose sole purpose is to put back older boards into a safe state as quickly as possible. With board that follow the spec, that would not be necessary.

at67 wrote: 26 Oct 2021, 18:28 Warm reset...Marcel intentionally coded his reset routines the way he did so that the firmware, (native code), would not need to change for each new piece of hardware that was introduced, (just add your initialisation code to the GCL reset routine, i.e. warm reset). Changing the early initialisation native code can be much more problematic then just adding a some extra vCPU code to the GCL reset, (even though you still need to burn a new ROM for both).
Well, I cannot issue ctrl code 0x7f from the vCPU because SYS_ExpanderControl filters those. But I can do from SYS_Reset which is the last thing before calling the vCPU reset code. What I do not understand are the criteria for doing some basic things in the cold reset part, and some equally basic things in the warm reset code in SYS_Reset.
at67 wrote: 26 Oct 2021, 18:28 I'm confused are the MISOn outputs open collector/open drain or push pull? If they are open then you can tie them all together with one pull-up resistor and no diodes, (check the data sheets!), if they are push-pull then you need the steering diodes and once again only one pull up or pull down resistor, (the direction of the diodes and the choice of pull up or pull down depend on whether you are implementing a wired OR, or a wired AND).
Well, SPI is very much a de-facto standard. There is no official spec. Just many implementations. The most common view is that devices should never drive the MISOx lines when they're not selected. Other documents suggest a weak pull-up with a 50k resistor to make sure that they have a CMOS-friendly state when nobody drives them . Then you have devices, like SD cards, that can be put in SPI mode, but otherwise ignore the device select line or worse use it for other purposes, and can do things while not selected(*). Finally you have programmers who assert two device select lines at the same time. Therefore one cannot tie the MISOx together without some circuitry to prevent shorts. The most precise solution is to logically gate the MISOx lines using the states of the corresponding SSx lines. The wired-or also prevents shorts but in a weaker manner: a rogue device that pulls down its MISO line while unselected prevents all other devices from speaking. I2C works like that.

(*) When I realized that SD cards can theoretically do things without being selected (provided they read a valid CRC), I modified my board design to make sure the SD card does not receive a clock signal when not selected. Then I found out that in order to put a SD card in SPI mode, the spec suggests to cycle the clock at least 70 times while the SD card is not selected. The SD cards I have work very well without this. But just in case, I changed the GAL program to suppress the clock when the other SPI port is selected. This is the best I can do...
lb3361
Posts: 360
Joined: 17 Feb 2021, 23:07

Re: Extending the CTRL code space

Post by lb3361 »

Submitted pull request https://github.com/kervinck/gigatron-rom/pull/214.

Merged with commit : https://github.com/kervinck/gigatron-ro ... 1b5a61fd28

I propose to use this forum topic to keep track of the usage of the ctrl special code space.
lb3361
Posts: 360
Joined: 17 Feb 2021, 23:07

Re: Extending the CTRL code space

Post by lb3361 »

The "RAM & IO Expansion v7 Dual Drive" is a hardware proof of concept for this proposal.

https://github.com/lb3361/gigatron-lb/t ... sion-retro

This board recognizes the special ctrl code and, instead of modifying the ctrl bits, exposes the special code on a new 13 pins header. The github discusses how to use these signals to implement additional output ports with a single chip or attach a digital-to-analog converter. The board also provides a new SD Card header for a second SD Card (hence "Dual drive"). Many thanks to Hans61 who discovered that we should not only pay attention to the poor SPI support of SD cards, but also to the even poorer support implemented in cheap SD Card breakout. Fortunately a software solution was possible with both the v6 and v7 boards. But using multiple SD Cards, or even one SD Card and another SPI device, does not work reliably with Marcel's original RAM & IO expansion board.

Here is a video of the dual drive board in operation : https://youtu.be/4QJxs8xd_jk

20211118_203803.jpg
20211118_203803.jpg (2.38 MiB) Viewed 2337 times

Garage sale for things I no longer need (at postage price) : I have four extra PCB for the v7 board. I also have two spare GALs that I can pre-program. Finally I have two assembled v6 boards that only have one SD port but can use a second SD card breakout attached the a different header. Please PM me if you want some of these items.
Hans61
Posts: 102
Joined: 29 Dec 2020, 16:15
Location: Saxonia
Contact:

Re: Extending the CTRL code space

Post by Hans61 »

I have already tested a 74HTC377 with 8 LEDs at the output on the new 13-pin header extension. It works very well.
Bits: connect pins D[1..8] to the address lines A[8..15], pin CLK to /AUXCTRL, pin /G to /AUXDEV0.
A binary counter runs with the following code:

Code: Select all

SYS_ExpanderControl_v4_40  EQU     0x0b09

sysFn                      EQU     0x22

temp                       EQU     0x81
counter                    EQU     0x83

      ; set counter to start 0
      LDI     0
      ST      counter
      
loop  LDWI    SYS_ExpanderControl_v4_40
      STW     sysFn
      
      LD      counter     ; increment counter
      ADDI    0x01
      ST      counter
      
      LDI     0x00        ; DEVADDR0 = 0x00, DEVADDR1 = 0x10
      ST      counter - 1        
      LDW     counter - 1 ; vAC low byte = DEVADDRx, high byte = data = couter
      SYS     40
      
      LDWI    -1000       ; set time for wait a moment
      STW     temp
wait  LDW     temp        ; delay
      ADDI    0x01
      STW     temp
      BNE     wait
      
      BRA     loop
Post Reply