Hi Felix At the moment, my toolchain of choice is pen-and-paper. I'd like to have a reasonably clear idea of the necessary micro- architectural features before starting to write code. One thing I have decided upon is that I would start with a fully microcoded design. No doubt it will be SLOW, but that's something to improve upon later... isn't Uxn meant to stay around for 100+ years? :) This way it will be also easier to catch up if the ISA still evolves. And it might turn out to be a design simple enough to be understandable _in full_ by a hobbyist. I think there would be value in that. Once it does come to writing the code, I might look at some of the fancy modern HDLs, or just go with SystemVerilog. I would be targeting the (awesome) Yosys toolchain and the open-hardware ULX3S board. I'll report back when I have something tangible to show off ;) Just one question... is there any kind of test suite for the Uxn ISA? The tougher, the better -- or basically any body of CPU-heavy code that I could throw at a prototype implementation to test it for compliance/ regressions, as that's always a big concern in logic designs. Appreciate your interest! Best, Martin
> At the moment, my toolchain of choice is pen-and-paper. That's the best toolchain, indeed. :-) > Just one question... is there any kind of test suite for the Uxn ISA? > The tougher, the better -- or basically any body of CPU-heavy code that > I could throw at a prototype implementation to test it for compliance/ > regressions, as that's always a big concern in logic designs. There is projects/utils/test.tal in the uxn repository and I've found this via awesome-uxn: https://github.com/DeltaF1/uxn-instruction-tests That's all I personally know of, there might be more. felix
Pen & Paper is definitely the way to go for this :) I'd love to follow along, it's also things I know nothing about, but I'd like to learn more about hardware. This testing program only requires that you have the DEO and EQU opcodes to get started: https://git.sr.ht/~rabbits/uxn5/tree/main/item/etc/tests.tal
Hello again I am getting closer to having a baseline implementation of bulk of the CPU spec, meaning all instructions, stacks, RAM+device memories and the 3 System traps. Thus far implemented as a hardware model in Python: https://github.com/mcejp/lux (you will see very quickly that this is not just another run-of-the-mill emulator :) There is a long road ahead both in terms of hardware feasibility and execution speed (I haven't even added a direct datapath from Top-of- Stack to ALU... not good!) I have not started documenting things, either. Synthesizable code is still far off, but if you're curious, you can start playing around with the model today and probably find a good number of bugs. Instructions are in the README. Right now the CPU can run all of uxn-instruction-tests, and most of Devine's tests.tal (choking only on the stack under/overflow) I used Hylang to build my microcode, by the way. It's a very neat LISP-in-Python, perfect for DSLs and worth checking out. (I hope that this time my reply does not spawn yet another thread :| Sorry.)
I'm thrilled to see this come together, I don't have the internet bandwidth to clone anything at this moment, but I'm excited to follow along the repo until we sail back to civilisation. You're the first person to use the uxntal test rom, do you have any suggestion that you'd like to see, I haven't really shared it yet, and I'd love to improve it if possible. There's also no uxntal implementation guide, if you have some pointers, or places that tripped you, let me know. I'd like to take this opportunity to better document the project as well. Since you're not planning on covering devices, would something like busy beaver help you test things out? Are you using LED lights or something to validate if a program is working?
The test rom is going to be immensely helpful, I’m working on reimplementing uxn in rust, as a project. > On Jun 25, 2022, at 8:54 PM, Hundred Rabbits <email@example.com> wrote: > > I'm thrilled to see this come together, I don't have the internet > bandwidth to clone anything at this moment, but I'm excited to follow > along the repo until we sail back to civilisation. > > You're the first person to use the uxntal test rom, do you have any > suggestion that you'd like to see, I haven't really shared it yet, and > I'd love to improve it if possible. > > There's also no uxntal implementation guide, if you have some > pointers, or places that tripped you, let me know. I'd like to take > this opportunity to better document the project as well. > > Since you're not planning on covering devices, would something like > busy beaver help you test things out? Are you using LED lights or > something to validate if a program is working?
In my view the main strength of the test ROM is that it is plain Uxn code with minimum runtime requirements. What surprised me was that the test begins by mapping the stacks to RAM. Isn't this a Varvara-ism, i.e. outside of Uxn spec? Also, if the under/overflow vector test fails, no error is printed. Ultimately, though, I feel like the approach of a hand-written ROM can never be sufficiently comprehensive to thoroughly test non-simple CPU implementations. For example, there is no testing of k/r modes, and to add it in you would basically need to increase the amount of code 4x. If we really care about validation of implementations, it would be incredibly useful to have some parse-able (!) formal specification of how the machine state is transformed by each instruction. For example (syntax arbitrary): opcode ADD: if pre.wst_pos < 2: # stack underflow must error out post.wst_pos == 0 post.wst_err == 1 post.pc == pre.system_vector else: # standard byte Add post.wst_pos == pre.wst_pos - 1 post.wst[post.wst_pos - 1] == (pre.wst[pre.wst_pos-1] + pre.wst[pre.wst_pos-2]) & 0xff From this, one can generate a heap of test vectors + assertions focusing on the edge cases (near-stack-overflow etc). Though probably not all important state is observable from inside the VM, so interpreter cooperation would be necessary. Some things that tripped me up in implementation: - how different instructions are affected by S-mode - how k-mode works - byte vs short access to device space (turns out it works just like RAM) - multiply and especially divide are much more expensive in hardware than other ops. But for a generic VM it makes sense to have them in the instruction set. In the end I mostly looked at uxn_eval() as a source of truth. As I'm not testing anything on hardware yet, I don't have LED outputs. When necessary, I just generate micro-op traces on stdout.
I should probably have removed the System/wst deo from the tests, I wrote this test rom for a varvara implementation, but I could remove it, it hardly uses it. I had the idea of making this test rom to get people started, after I saw that the chip8 implementations often worked that way. I don't know anything about assertion, verification, all that sort of thing, so I probably won't be the person to write an implementation guide. Especially since i've been neck deep into this stuff for so long now, that I have trouble looking at this from an outside perspective, that's why I think you're at the perfect place, if you're up for it, to improve the documentation. Could you explain the k mode in your own words in that way that could be useful to other implementers, so I could put it on the docs? Is there a way we could make the uxn.c more explicit? > byte vs short access to device space What do you mean by that? Thanks for the feedback :) I think this could be vastly improved.
On Thu, Jun 30, 2022 at 15:42:59 -0700, Hundred Rabbits wrote: > Could you explain the k mode in your own words in that way that could > be useful to other implementers, so I could put it on the docs? Is > there a way we could make the uxn.c more explicit? I'm not Martin, but my own thoughts: (pretty much a translation of uxn.c): Normally, operations get values to work on by POPping sequentially, but the k mode causes POP to do nothing to the stack, or it POPs from a copy of the stack. So any operation which needs to read some value, even if it will leave it there, would usually a = POP(); PUSH(a); ...other operations using a but in the k mode, it's more like a = PEEK(); PUSH(a); ... This requires a slightly different way of thinking about some opcodes: for example, DUP: instead of saying it pushes the value on the top of the stack, say that it reads (pops) the top value, pushes it back on, and pushes the same value again. Hence #00 DUPk produces 00 00 00. Hm, that didn't turn out to be clearly worded or concise, or more understandable than what's currently in your docs. Ah well. I think the best way of understanding the modes is by inspecting the output of each opcode in each mode, as in the examples in the uxntal reference. However, that's not a formal specification. phoebos
Regarding k-mode, my model of it is that each stack has in fact separate read (pop) and write (push) pointers. Normally these are tied together, except in k-mode where they move independently. However, they are still connected with a bungee cord which contracts at the end of every instruction, teleporting the read pointer to the write pointer. This model works since you never have POP-after-PUSH from the same stack within any single instruction. In fact, this is how I implement it in hardware (minus the physical bungee rope)