SteffenReith / J1Sc

A reimplementation of a tiny stack CPU
BSD 3-Clause "New" or "Revised" License
77 stars 7 forks source link

Documenting the Files #2

Open PythonLinks opened 11 months ago

PythonLinks commented 11 months ago

There are many files in J1Sc/src/main/scala defining the circuit. This documentation is an overview of what those files do. For simplicity I deleted the .scala from the file names.

J1: This is the highest level of the J1, defining interfaces to the outside.
J1Core: Defines signals towards the inner part of the J1.

INNER WORLD

J1Stack: Basic stack definitions and logic.

J1RStack: Return stack definition and logic, all in one place.

J1DStack: Data stack definition and logic.

J1Decoder: Decides the instruction. Just decides if it is a literal, call, jump, conditional jump or instruction.

MainMemory: Defines the main memory assuming multiple smaller blocks (???)

J1PC: Program counter

Timer: Sets alarms

J1Alu: Does the basic operations (Words).

J1Bus: I think that there are multiple busses, but I am not yet sure. One for the LEDs, one for the GPIO, PWM, LEDs, and SSD.
J1BusSlaveFactory:

OUTER WORLD

DBPinArray: debounced Pin array. Input Pins, Debounced Pins, a timer, and some kind of bus.

GPIO: Define the GPIOs. Assumes that we are using PMODs.

LEDArray: A register of LED values which you can read and write to. Why does in mention PWM?

InterruptCtrl: Defines the interrupts. I did not fully understand this part.

PWM: Pulse Width Modulation is for control of motors. The width has to be a power of 2.

SSD: For driving a Solid State Drive.

JTAG FOR SINGLE STEP DEBUGGING

J1Jtag: J1JtagData: SingleStepUnit:

Board Definitions

Here is where you customize things.

J1Config: Configure IRQ, JTAG. Timer, Instruction set and the J1 itself. Includes instruction set tests. CoreConfig.: Configure UART, LEDs, PWM, and GPIO for various boards.

 *J1Ice.: Define a J1 for the Ice Board. J1Ico** Define a J1 for the Ico board.

J1Nexys4X: Define a J1 for the Nexys4X board.

If you like this I can create a pull request.

PythonLinks commented 11 months ago

So I made some small edits to the above issue. Mission accomplished, I now know what all of those files are and where to edit them.

I do love your prose coding style. Of course that means I have not learned any SpinalHDL or Scala, but that will not be a problem.

I really like SpinalHDL. MyHDL was terrible, took a real functional approach rather than an object approach. It could generate the Verilog, or VHDL, but there was no object model one could build apps on. Migen must have a problem, which is why Amaranth was forked from it. Amaranth at first glance also looks questionable. I did not spend that much time with it but there is an odd elaborate function. I think each command here actually builds the object model. One can then simulate at the Scala level, and run queries over the object model. Time will tell.

Ideally it would be good to have a Forth in Scala, simulate at the Forth level, compare it to a simulation at the J1Sc Scala level, and compare it to a simulation at the gate level using verilator. I wonder if that is doable? I do not yet know enough to judge it.

I do wonder if this approach is as efficient as the J1. What James Bowman did was one giant casex statement. Here you have a much more understandable top level: Literal, call, jump, conditional jump or instruction, and then another place for the instructions. And the cpu flags are similarly dealt with separately here. Infinitely easier to understand, but I worry that the generated circuit will be larger. I will probably do both a SpinalHDL version of some circuits and a pure Verilog version of the same circuits to make sure that the base resource utilization is the same.

My posting the other day was not quite clear. The J1 does their outer interpreter in hardware. But they wrote the blinky by defining values in memory which was then synthesized. I want to do something similar to their blinky. Find the code for the outer interpreter, white a python script which converts it into syntheziable memory verilog instructions, and then include that verilog. Then I get a chip which runs a basic Forth, but has no libraries installed. People can then install whichever libraries they want just by loading it into the outer interpreter. They can use SwapForth, Mecrisp Forth, or something else.

In a private email we discussed memory. As Steffen said it is completely confusing how the J1 manages it on devices without true dual port RAM. Here is some documentation. https://github.com/jamesbowman/swapforth/issues/74

The problem is in running the J1 on the ICE40 devices. The larger ICE40 UP5K have 4 blocks of single port RAM and 30 * 4kbit blocks of pseudo dual port ram PSDPRAM. The PSPDPRAM can read and write at the same time, but cannot do two reads at the same time. The J1 reads an instruction every clock cycle, so when reading from application memory, the J1 has to be paused for a cycle or two. But it can still write to application memory with out pausing.

My understanding is that the J1, and the Mecrisp cores cannot use the ICE40 single port memory. But that should not be too hard to fix. Stall the device when application memory is read or written. I suspect that you may already do this. Is that correct?

One more point. My understanding is that the Icestorm bitstream can load memory into the 30 * 4kbit PSDPRAM memory blocks, but not into the Single Port RAM. If I want to do 4 cores, then each would get 1 single port memory block, but the lowest 4Kbits would actually be one of the 30 PSDPRAMs.

So far I am having a wonderful experience with the J1Sc. And SpinalHDL is also looking more promising.