OSR8 Design and Development

© 2020-2026 Kevan Hashemi, Open Source Instruments Inc.

[11-DEC-20] For initial development of the processor for the A3035A, see its Development Page.

[03-FEB-21] We add an instruction "ld B,(nn)" and find the OSR8 increases by 10 LUTs. We remove, then add instead "ld B,A", which adds 30 LUTs.

[25-APR-21] We are working on the OSR8V2 for the LCMXO2-7000 of the A3038BB. We increase the program memory from 1 KByte to 8 KByte, increasing all code memory pointers from ten bits to thirteen bits. The total size of our P3038BB firmware drops from 1081 to 1072 LUTs as a result of this increase.

[30-APR-21] The combinatorial implementation of the interrupt set and reset signals in our P3038BB01A firmware produces erratic set and reset actions as our OSR8V2 is running a demonstration program with a periodic interrupt. We change the implementation of the set and reset signals to registered under the control of the CPU clock and they behave well. We implement the same change in the P3035A10 firmwarwe top produce the prototype P3035A11. We set IRQ0 (interrupt request zero) when the IRQ timer first reaches zero, so that we generate only one interrupt request every time the timer runs down, even though the timer runs off RCK (32.768 kHz) and the processor runs off PCK (10 MHz). With LCMXO2-7000HC-4, max PCK is 17 MHz, with -5 is 20 MHz, and with -6 is 22 MHz. We have the -5 for the A3038BB-A. Our BB01A final code fades the white LEDs in and out using a 128-Hz interrupt and a continuous main loop. It exercises the test points to show interrupt execution and the 128.000 Hz interrupt clock.

[12-MAY-21] We test "seti" and "clri" and find they work to disable and enable interrupts. We have a 6-μs pulse on a test point between "seti" and "clri". We have interrupts at roughly 2 kHz at random. We never see the pulse interrupted. The interrupts themselves execute immediately after the end of the pulse.

seti
ld A,(test_point_addr)
or A,0x04                    
ld (test_point_addr),A  
ld A,100
dly A
ld A,(test_point_addr)
and A,0xFB
ld (test_point_addr),A 
clri

[07-APR-22] The new OSRV3 provides control over its process and program memory size through generic constants, as well as the start and interrupt locations, and the stack pointer base and top. We use this more flexible version as we are developing the A3041A, where we are adjusting the memory sizes as we go. By adjusting its constants, the V3 code is backward compatible with the V2 and V1 code.

[09-APR-22] We try the OSRV3 with the A3035A code. The processor does not run. We trace the problem to the generic map in the declaration of the OSR8 entiti. Here is our attempt to make the CPU flexible through generic constants. Only by removing the generic map can we get the CPU to run. If we define two generic constants that we don't even use, and leave all other code the same, the processor won't run. For example, the addition of prog_addr_top and cpu_addr_top in the following code, two constants we never use, stops the processor from running.

[11-APR-22] Upgrade to Lattice Diamond 3.12, now find we can use generics, and identify a compiler bug in the interrupt handler. Proceed with further improvements to the OSR8V3. Delete the rst_cpu instruction, which we never implemented, and has been replaced by writing to a sofware reset register.

[12-APR-22] Add generic constants to the OSR8V3, and all goes well with the new compiler. The OSR8 may now be configured from the main unit.

[29-JUN-22] The OSR8V3 has undergone further changes in its adaptation to the A3041. We are recording development in a GitHub repository, P3041. From VHDL comments, "Remove stack overflow flag. Stack pointer always resets to zero, so CPU must have RAM at address zero to use as initial stack during initialization. The initial stack will allow the CPU program to load the stack pointer with a new value."

[30-JAN-26] We discovered a bug in the OSR8 reset behavior last year, in which we see the program memory being reset even as the first program byte is being read. Having corrected this bug, we are able to remove certain "others" clauses from our case statements and the OSR8 is still stable.

[11-FEB-26] We remove the redundant intermediate variable "prog_addr" and create OSR8V4 for the latest P3041 (IST firmware) and the new P3054 (IPT firmware). The problems we have been encountering with interrupts failing after making minor adjustments to the code turn out to be due to clashes between TCK and RCK in the interrupt manager of the P3041 code, which we now solve by clearing interrupt bits asynchronously and setting them only with RCK.

[15-FEB-26] We are encoutering instability in the OSR8 behavior while working on our P3041 firmware. We remove unused code and the OSR8 interrupts stop working. We restore the unused code and they start working again. Such instability is a well-known symptom of unconstrained logic signals in VHDL. Unlike languages like ABEL, VHDL does not support "dont' care" as a signal value. If combinatorial output Y has no defined value for input A, an ABEL compiler will treat the value of Y for A as "don't care" and will optimize the logic to make best use of its freedom to choose the value of Y for A. But in VHDL, the compiler says, "when A occurs, Y must retain its previous value", which means the compiler will start to introduce latches into the combinatorial logic, so that the logic is no longer combinatorial, but contains memory elements. These memory elements are vulnerable to glitches on their enable inputs. Sometimes, the compiler can prove to itself that input A will never occur, and so it does not add the latches. Other times, the compiler fails to realise that A will never occur, and so adds the latches. But if A can occur, the compiler will most certainly add the latches. We must eliminate these latches for certain. In our registered state machines, we find that adding "others => null" to case statements changes the size of the compiled code. Sometimes the code gets bigger, sometimes smaller. This is despite our declaring default values for all registered signals at the start of our rising edge process. In principle, adding "others => null" to a case statement for signal X should do nothing if we have preceeded the case statement with "X <= '0'". In VHDL, subsequent statements do not clash with previous statments, they over-ride previous statements. So we might have "X <= '0'" followed by "if A then X <= '1'" and the second statement over-rides the first whenever A is true, but otherwise X <= '0'. But something is not clear to the compiler when it comes to case statements over-riding or not over-riding defaults. So we add "others => null" to all case statements. We try specifying default values for cpu_data_out. We see a 20-LUT increase if we declare a default value of zero compared to no default. If we specify a default value of "cpu_data_out <= cpu_data_out", as in "by default don't change", we see no difference in code size between this specification and no specification, which is what we expect. All this to say that we constrain our code in order to make it deterministic. But the result is the code grows by 50 LUTs and we cannot fit it in the 1280 LUTs of the A3041's LCMXO2-1200ZE.

[16-FEB-26] We have to shrink the OSR8. We eliminate eight instructions that we we have never used in any of our OSR8 programs so far. These are "inc E", "dec E", "inc H", "dec H", "inc L", "dec L", "inc SP" and "dec SP". The increments and decrements of the registers E, L, and H can be duplicated exacly by a sequence like this "push A; push E; pop A; inc A; push A; pop E". The "dec SP" and "inc SP" instructions we cannot duplicate perfectly, but we can do "ld HL,SP; push H; push L; pop IX" and now we have SP in IX. We can increment and decrement IX to point to locations in the stack where we might have placed procedural parameters. And indeed, this use of IX feels like a cleaner way to get to stack-resident parameters than manipulating the stack pointer. With these eight instructions eliminated, the OSR8 shrinks by 100 LUTs, and now fits with the P3041 logic in the LCMXO2-1200ZE.

[19-FEB-26] We add an Interrupt Service (ISRV) output from the OSR8 to indicate that it is servicing an interrupt. This signal is asserted on the falling edge of CK. In the P3041 Boost Controller and Clock Controller we use ISRV to put the CPU into boost immediately it begins an interrupt and as soon as it finishes the interrupt. We remove all integer types from the OSR8, using unsigned types instead, which eliminates many integer type casts, abbreviates the source code, and allows us to express the ALU operations more directly. We change the opcodes to standard logic vectors. With the simplified OSR8V4, our P3041 code shrinks from 1233 LUTs to 1201 LUTs. The code is robust and running all features of the P3041. We release V4.2.