Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SPI EEPROM simulator for W25X20CL #2

Closed
vintagepc opened this issue Apr 6, 2020 · 11 comments · Fixed by #56
Closed

Add SPI EEPROM simulator for W25X20CL #2

vintagepc opened this issue Apr 6, 2020 · 11 comments · Fixed by #56
Assignees
Labels
Hardware sim Simulated hardware

Comments

@vintagepc
Copy link
Owner

Per title. Right now the F/W boots with 'W25X20CL is not responding..." message.

@vintagepc vintagepc added the Hardware sim Simulated hardware label Apr 6, 2020
@vintagepc
Copy link
Owner Author

vintagepc commented Apr 9, 2020

Helpful hints:

  • I screwed up the existing stub. The correct SPI connection call is

avr_connect_irq(avr_io_getirq(avr,AVR_IOCTL_SPI_GETIRQ(0),SPI_IRQ_OUTPUT), hw.spiFlash.irq + IRQ_W25x20CL_SPI_BYTE_IN);

You did comment you're not as familiar with general programming - For command-register style stuff like you see with SPI/I2C, and many other embedded protocols, I'd suggest learning about bitfields and unions. These are a lot nicer/neater to manipulate (and maintain) than a bunch of &-ed or |-ed bitmasks littered throughout the code. Think of it like being able to "label" parts of a base data type. (From a technical perspective, unions let you interpret the same piece of memory with different data types, and we take advantage of the field-width feature of structs together with this). It leads to some really elegant code that's dead simple to read. (At some point I'll probably clean up the other ones that are in the example code too)

For example - if you have something that takes an 8-bit command, of 2 bits are an opcode, and the other six some sort of memory address, you would define something like this:

typedef union myCmd_t
{
    uint8_t byte;
struct 
{
    uint8_t opcode :2; // trailing :2 is the width of this field.
    uint8_t addr :6;
} bits;
} myCmd;

Then, you can use it in a way that's very easy to read for input:

    myCmd cmd;
    cmd.byte = read_wire();
    if (cmd.bits.opcode == OPCODE_DO_SOMETHING)
    {
        thing->address = cmd.bits.addr;
    }

or output:

myCmd cmd;
    cmd.bits.opcode = 0xF3;
    cmd.bits.addr = 244;
    thing.sendData(cmd.byte);

and there's no messy 2^n enums, and basically zero chance of accidentally using the wrong bitmask!

@vintagepc
Copy link
Owner Author

I have also pushed up a partial branch with some TMC2130 work in it that may be of use for reference. https://github.com/vintagepc/MK3SIM/tree/%238_TMC2130_Sim

@vintagepc
Copy link
Owner Author

Just wanted to check in - I know it's still early stages but don't hesitate to ask if there's something I can clarify or assist with.

If you'd like something smaller to get familiar with the architecture first, #13 is probably a good simple task to start.

@leptun
Copy link
Collaborator

leptun commented Apr 14, 2020

I was really busy with some MBL testing recently. I still don't understand this code quite well. What reference manual are you using for all the functions and their usage?

@vintagepc
Copy link
Owner Author

vintagepc commented Apr 14, 2020

Sure, no worries - Just wanted to make sure it wasn't off-putting because this can be a pretty complicated task if you don't know where to start.

I'm primarily going off the examples and unit test code, as well as interpreting the .h files. the actual function documentation is pretty bad in that regard, it's basically just a list of the functions and the call stack with no info on what the arguments mean or are so I gave up on it. In some cases I've searched the code for a certain keyphrase (e.g "SPI", and then dumped the resulting relevant looking item (SPI_IRQ_OUTPUT) into google or github search to find examples.

VS code with intellisense helps a bit if you know roughly what you're looking for.

The primary thing any implementation/hardware boils down to is adding IRQ table entries for the various inputs/outputs (be they SPI, digital pin changes, PWM pin changes, etc.).

Each part has listeners that act on input signals (avr_irq_register_notify()) and basically just passes out other signals (avr_raise_irq()). So within the context of an object it is basically a standalone entity that knows nothing of the outside world.

You'll definitely need the datasheet for the flash chip so that you know what its SPI commands are. I'd build things up step by step from scratch - an example workflow, basically what I went through with the TMC drivers:

  • Implement a simple dummy chip that just listens and printfs out any bytes it receives over SPI - just to make sure it's wired up right and can receive.
  • Look at the firmware source to see what it is sending to the flash chip as its first command.
  • Notice it's also getting SPI data for other items on the SPI bus.
  • Look up the info on the chip select signal for the flash chip, add an IRQ for it and wire it in.
  • Use it to filter down to just commands relevant to it.
  • Just start working through the commands one by one to get familiar with things... e.g. see if you can send a response to the initial "who are you" query the firmware sends the flash chip.
    Might be helpful to alter the firmware and add some debug output to the startup so you can make sure that it's getting what you think you're sending it.

I think most of the relevant bits are available for the heavier lifting in other places - the TMC branch as noted earlier, some bits of the Einsy EEPROM for how to persist the data across reboots, etc.

@leptun
Copy link
Collaborator

leptun commented Apr 14, 2020

I’ll take a look later into implementing the spi flash. It should be much easier than the tmc drivers.

Btw, I was wondering what performance you get out of the simulator. The thing is that I run a VM with ubuntu at the moment and the emulation moves really sluggish. If you run linux without a VM, is it worth it?

@vintagepc
Copy link
Owner Author

It is kinda slow for me (though the primary point of reference I have is just reaction to input - encoder twists, knob pushes take 1-2 seconds to register). Hard to say whether that is because of the simulation or the firmware waiting and timing out on responses from things (such as the TMCs) that it's not getting. So I'm hoping it will get better as we get more hardware implemented.

at some point I will try to do a performance profile to see if I can figure out why. but I've found that adding einsy_eeprom_poke(avr, EEPROM_CRASH_DET,0x00) helps a bit - right now it's constantly detecting crashes and trying to re-home which will pause the display until it fails.
The Einsy runs at 16 MHz, I can't imagine that we cannot sufficiently emulate that on a mult-GHz machine.

@vintagepc
Copy link
Owner Author

vintagepc commented Apr 14, 2020

One other thing of note is that in the simreprap branch there's a comment that marlin does not have a sleep in its idle state so it's constantly running full throttle. That's definitely not helping the situation since even when idle the main loop is going to compete with any ISRs for cpu time.

@leptun
Copy link
Collaborator

leptun commented Apr 15, 2020

I'd say that reading now works quite nicely. The xflash file is also persistent, so that's nice.
I think that my only option for the moment for loading multi-lang firmware is using avrdude-slic3r to load a hex file build with PF-build.sh.
@vintagepc What changes did you do to the source to get it to work with the emulator?

w25x20cl_t: CSEL changed to 00
w25x20cl_t: byte received: 03
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: 00
w25x20cl_t: command:03, addr:00000
W25X20CL: Clocking out a5
w25x20cl_t: command:03, addr:00001
W25X20CL: Clocking out 5a
w25x20cl_t: command:03, addr:00002
W25X20CL: Clocking out b4
w25x20cl_t: command:03, addr:00003
W25X20CL: Clocking out 4b
w25x20cl_t: command:03, addr:00004
W25X20CL: Clocking out 6e
w25x20cl_t: command:03, addr:00005
W25X20CL: Clocking out 24
w25x20cl_t: command:03, addr:00006
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:00007
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:00008
W25X20CL: Clocking out f6
w25x20cl_t: command:03, addr:00009
W25X20CL: Clocking out 09
w25x20cl_t: command:03, addr:0000a
W25X20CL: Clocking out 73
w25x20cl_t: command:03, addr:0000b
W25X20CL: Clocking out 63
w25x20cl_t: command:03, addr:0000c
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:0000d
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:0000e
W25X20CL: Clocking out 38
w25x20cl_t: command:03, addr:0000f
W25X20CL: Clocking out e0
w25x20cl_t: CSEL changed to 01
W25X20CL: Clocking out ff
w25x20cl_t: CSEL changed to 00
w25x20cl_t: byte received: 03
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: 24
w25x20cl_t: byte received: 6e
w25x20cl_t: command:03, addr:0246e
W25X20CL: Clocking out a5
w25x20cl_t: command:03, addr:0246f
W25X20CL: Clocking out 5a
w25x20cl_t: command:03, addr:02470
W25X20CL: Clocking out b4
w25x20cl_t: command:03, addr:02471
W25X20CL: Clocking out 4b
w25x20cl_t: command:03, addr:02472
W25X20CL: Clocking out 5b
w25x20cl_t: command:03, addr:02473
W25X20CL: Clocking out 29
w25x20cl_t: command:03, addr:02474
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:02475
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:02476
W25X20CL: Clocking out d2
w25x20cl_t: command:03, addr:02477
W25X20CL: Clocking out 92
w25x20cl_t: command:03, addr:02478
W25X20CL: Clocking out 65
w25x20cl_t: command:03, addr:02479
W25X20CL: Clocking out 64
w25x20cl_t: command:03, addr:0247a
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:0247b
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:0247c
W25X20CL: Clocking out 38
w25x20cl_t: command:03, addr:0247d
W25X20CL: Clocking out e0
w25x20cl_t: CSEL changed to 01
W25X20CL: Clocking out ff
w25x20cl_t: CSEL changed to 00
w25x20cl_t: byte received: 03
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: 4d
w25x20cl_t: byte received: c9
w25x20cl_t: command:03, addr:04dc9
W25X20CL: Clocking out a5
w25x20cl_t: command:03, addr:04dca
W25X20CL: Clocking out 5a
w25x20cl_t: command:03, addr:04dcb
W25X20CL: Clocking out b4
w25x20cl_t: command:03, addr:04dcc
W25X20CL: Clocking out 4b
w25x20cl_t: command:03, addr:04dcd
W25X20CL: Clocking out df
w25x20cl_t: command:03, addr:04dce
W25X20CL: Clocking out 28
w25x20cl_t: command:03, addr:04dcf
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:04dd0
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:04dd1
W25X20CL: Clocking out b7
w25x20cl_t: command:03, addr:04dd2
W25X20CL: Clocking out 31
w25x20cl_t: command:03, addr:04dd3
W25X20CL: Clocking out 73
w25x20cl_t: command:03, addr:04dd4
W25X20CL: Clocking out 65
w25x20cl_t: command:03, addr:04dd5
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:04dd6
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:04dd7
W25X20CL: Clocking out 38
w25x20cl_t: command:03, addr:04dd8
W25X20CL: Clocking out e0
w25x20cl_t: CSEL changed to 01
W25X20CL: Clocking out ff
w25x20cl_t: CSEL changed to 00
w25x20cl_t: byte received: 03
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: 76
w25x20cl_t: byte received: a8
w25x20cl_t: command:03, addr:076a8
W25X20CL: Clocking out a5
w25x20cl_t: command:03, addr:076a9
W25X20CL: Clocking out 5a
w25x20cl_t: command:03, addr:076aa
W25X20CL: Clocking out b4
w25x20cl_t: command:03, addr:076ab
W25X20CL: Clocking out 4b
w25x20cl_t: command:03, addr:076ac
W25X20CL: Clocking out ce
w25x20cl_t: command:03, addr:076ad
W25X20CL: Clocking out 29
w25x20cl_t: command:03, addr:076ae
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:076af
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:076b0
W25X20CL: Clocking out f0
w25x20cl_t: command:03, addr:076b1
W25X20CL: Clocking out 0f
w25x20cl_t: command:03, addr:076b2
W25X20CL: Clocking out 72
w25x20cl_t: command:03, addr:076b3
W25X20CL: Clocking out 66
w25x20cl_t: command:03, addr:076b4
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:076b5
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:076b6
W25X20CL: Clocking out 38
w25x20cl_t: command:03, addr:076b7
W25X20CL: Clocking out e0
w25x20cl_t: CSEL changed to 01
W25X20CL: Clocking out ff
w25x20cl_t: CSEL changed to 00
w25x20cl_t: byte received: 03
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: a0
w25x20cl_t: byte received: 76
w25x20cl_t: command:03, addr:0a076
W25X20CL: Clocking out a5
w25x20cl_t: command:03, addr:0a077
W25X20CL: Clocking out 5a
w25x20cl_t: command:03, addr:0a078
W25X20CL: Clocking out b4
w25x20cl_t: command:03, addr:0a079
W25X20CL: Clocking out 4b
w25x20cl_t: command:03, addr:0a07a
W25X20CL: Clocking out 21
w25x20cl_t: command:03, addr:0a07b
W25X20CL: Clocking out 29
w25x20cl_t: command:03, addr:0a07c
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:0a07d
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:0a07e
W25X20CL: Clocking out 3a
w25x20cl_t: command:03, addr:0a07f
W25X20CL: Clocking out 93
w25x20cl_t: command:03, addr:0a080
W25X20CL: Clocking out 74
w25x20cl_t: command:03, addr:0a081
W25X20CL: Clocking out 69
w25x20cl_t: command:03, addr:0a082
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:0a083
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:0a084
W25X20CL: Clocking out 38
w25x20cl_t: command:03, addr:0a085
W25X20CL: Clocking out e0
w25x20cl_t: CSEL changed to 01
W25X20CL: Clocking out ff
w25x20cl_t: CSEL changed to 00
w25x20cl_t: byte received: 03
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: c9
w25x20cl_t: byte received: 97
w25x20cl_t: command:03, addr:0c997
W25X20CL: Clocking out a5
w25x20cl_t: command:03, addr:0c998
W25X20CL: Clocking out 5a
w25x20cl_t: command:03, addr:0c999
W25X20CL: Clocking out b4
w25x20cl_t: command:03, addr:0c99a
W25X20CL: Clocking out 4b
w25x20cl_t: command:03, addr:0c99b
W25X20CL: Clocking out f8
w25x20cl_t: command:03, addr:0c99c
W25X20CL: Clocking out 25
w25x20cl_t: command:03, addr:0c99d
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:0c99e
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:0c99f
W25X20CL: Clocking out 0b
w25x20cl_t: command:03, addr:0c9a0
W25X20CL: Clocking out 5f
w25x20cl_t: command:03, addr:0c9a1
W25X20CL: Clocking out 6c
w25x20cl_t: command:03, addr:0c9a2
W25X20CL: Clocking out 70
w25x20cl_t: command:03, addr:0c9a3
W25X20CL: Clocking out 51
w25x20cl_t: command:03, addr:0c9a4
W25X20CL: Clocking out 01
w25x20cl_t: command:03, addr:0c9a5
W25X20CL: Clocking out 38
w25x20cl_t: command:03, addr:0c9a6
W25X20CL: Clocking out e0
w25x20cl_t: CSEL changed to 01
W25X20CL: Clocking out ff
w25x20cl_t: CSEL changed to 00
w25x20cl_t: byte received: 03
w25x20cl_t: byte received: 00
w25x20cl_t: byte received: ef
w25x20cl_t: byte received: 8f
w25x20cl_t: command:03, addr:0ef8f
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef90
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef91
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef92
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef93
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef94
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef95
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef96
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef97
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef98
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef99
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef9a
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef9b
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef9c
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef9d
W25X20CL: Clocking out ff
w25x20cl_t: command:03, addr:0ef9e
W25X20CL: Clocking out ff
w25x20cl_t: CSEL changed to 01
W25X20CL: Clocking out ff

@vintagepc
Copy link
Owner Author

Currently the only change is related to UDRE0 - I changed the UART0 code to use UART1 instead.
Then you can do a uart_connect_pty on UART1 and you should be able to talk to it with avrdude.

vintagepc added a commit that referenced this issue Apr 16, 2020
@vintagepc vintagepc reopened this Apr 16, 2020
@vintagepc
Copy link
Owner Author

Derp, did not want it to auto-close. >_<
I fixed the UART0 bug so you should be able to have more luck with optiboot now.

@leptun leptun mentioned this issue Apr 16, 2020
vintagepc added a commit that referenced this issue Jul 25, 2020
vintagepc added a commit that referenced this issue Jul 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Hardware sim Simulated hardware
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants