-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Add serial transport #56
Conversation
I would love to see this functionality added. It would be very useful to be able to use wchisp with the popular CH32V003 line, which only support UART with the factory bootloader. It'll probably also facilitate support for the recently-announced '002, '004, and '006 chips, This is actually a real coincidence, as I had been looking at the code myself recently to see what it would take to add serial support (and also to see if the serial protocol matches the USB), but concluded I don't have enough Rust experience to do it myself. And then this is posted today! :) By the way, a note of caution about the CH32V003 factory bootloader: it ignores the 'sectors' parameter of erase commands, because it only supports doing a full erase. (I guess they ran out of space in the BOOT flash to properly support 1K sector erase. Perhaps the newer '00x chips with their 3K bootloader will properly support it.) What this means is that when wchisp is doing a flash operation and attempts to erase only enough sectors to accommodate the given binary, instead every sector will always be erased. This should probably be documented somewhere if this serial support is to be added. |
The serial transport layer has been improved to no longer wait until all bytes are received. (The response contains the data length) @basilhussain I have not updated any documentation yet, but that is something important to mention. |
@Thom-de-Jong I think something got messed up with your last commit. It shows every line of each file as having changed. I'd guess something to do with line endings, as ignoring whitespace when viewing diffs shows something more sensible. |
I attempted to build and test this on my Windows system. Everything appeared to build successfully, but am getting crashes when I try and do anything significant (such as get chip info). I can successfully probe for serial ports:
But getting chip info, for example, crashes:
By the way, I have a suggestion: is it possible to change the permitted baud rate option values so that instead of "baud115200", "baud1m", "baud2m" they are just "115200", "1m", "2m"? Also - and forgive me if this is a stupid question - what's the actual need for a baud rate option? Surely a given device's serial bootloader only works at a specific, fixed baud rate? I'm not familiar with other CH32V chips, but for example the CH32V003 only works at 115200. Wouldn't it be more appropriate to have the relevant baud rate picked from the device info? |
@basilhussain Thank you for trying it out!
Unfortunately enums in Rust must start with an alphabetic or _ character, so "baud" is in my opinion a reasonably short option.
I'm using an enum because the WCHISP_Tool gives the option between 115200, 1m and 2m for my chip (CH32V203). We could allow the user to put in a numeric value but I don't know if the bootloader would accept anything other than these baud rates. (I don't check the response at the moment) The build-fail is fixed by not relying on libudev-sys. |
If you do not have a "rustfmt.toml" in your home dir,
|
I rebuilt with the latest changes and it appears to work fine for me now. Tested with a WCHLink-E as USB-UART adapter and CH32V003F4P6.
I note the 'info' command doesn't output any breakdown of the option bytes like for other chips. I assume this is because the |
@Thom-de-Jong I have made improvements to the device info YAML for CH32V00x and submitted PR against your fork, as I think these changes should go together. |
Expand CH32V00x device info YAML
I have checked if it is possible to use other baud rates and it seems to be possible to use any baud rate, as long as it is a reasonable rate (see Reference Manual 18.3) less than 2m. (On the ch32v203 at least) The response is always Should we let users specify the u32 they want to use or keep the enum with baudrates? |
I suspect the need for users to specify custom baud rates would be very minor, for two reasons. Firstly, not all serial bootloaders on all chips support the 'set baud' command, so the capability is pointless for those that don't. Secondly, I would think the primary motivation to use a different baud other than the default 115200 is to facilitate faster downloads, which is served by choosing one of the faster presets; in that scenario, why wouldn't you just go for the fastest preset? Further, if you've found that arbitrary bauds are not universally functional, then it's probably just inviting people to blame wchisp if their chosen rate doesn't work. 😛 So my opinion is that I don't think it's warranted. |
I thought I would just try and see what happens if you attempt to talk to a CH32V003 with 1M or 2M baud rate, and the result is that I think something is not working as expected. The initial 0xC5 set baud command gets sent at the expected 115200, and the bootloader as expected responds with a failure result (0xFE), but I'm not getting the expected "set_baud failed" error message. Instead I'm getting:
As I've said before, I'm a Rust noob, so I may be wrong, but it seems like it's failing to recognise that the command responded with failure (flashing.rs line 80), and then also failed somehow to set the baud rate on the serial port (line 81)? I'm not sure whether the latter isn't actually in my case fault of the WCH-LinkE adapter, because in separate testing trying to transmit at 1M through Tera Term, the max available preset listed in Tera Term for that port is 921600 (although I'm not sure where TT gets these listed presets). Manually forcing 1M gets me a TX bit rate that's inaccurate. The CH32V305 on the WCH-LinkE doesn't use an external crystal oscillator for clock (HSI only I presume), so it's probably unsurprising it's inaccurate at higher bauds, and why the COM port may not support that. In which case, perhaps I should take back my previous comment about support for arbitrary baud rates, because it appears even WCH's own programming tools don't support the current fixed presets! 😅 |
Based on WCHISPStudio I think the current implementation is fine, 115200 is supported by every device with serial programming support so I made that the default on fail. Others support 1m and 2m. Lets keep it this way for now, as choosing the baudrate just makes things a little faster, as you said. |
Sorry to be the bearer of bad news, but the baud setting code is still not working quite right. It's not failing anymore when the bootloader doesn't successfully acknowledge setting a different baud rate, but I'm not getting the expected "Custom baudrate not supported by the current chip" message:
I think the problem may be that Also, isn't the logic for calling |
Fixed set_baud logic
Implement serial header and checksum validation
I have an idea. Would it be possible to have the chunk size specified on a per-transport level? Perhaps make it an abstract property/method of Transport trait to be implemented by USB and Serial transports, and have that value be used by all methods in Flashing instead of hard-coded constant. If so, I propose to make it 56 bytes for USB, and 64 bytes for serial, for reasons I'll explain below. I note that both WCHISPTool and wchisp use a 56-byte chunk size when transferring data to be written or verified. I believe (correct me if I'm wrong) that this stems from the need to fit such data, minus overheads, into 64-byte USB packets. However, the serial UART transport does not suffer from such an inherent limitation. Using a chunk size of 56 bytes is actually fairly inefficient due to it not being an exact division of typical flash page sizes (e.g. 64 bytes for CH32V003, 256 bytes for CH32V30x). I know from studying the CH32V003 bootloader that it uses a buffering mechanism so that it is (wherever possible) able to write a full flash page at a time, and I expect bootloaders of other chips will be similar. And so because this chunk size is less than page size, it always takes at least two commands to send the data for every full page write. From study, and through experimentation (I changed In the case of the CH32V003, making the chunk size 64 bytes can make significant reductions in the number of write commands needed to download a firmware image, because essentially the buffering mechanism is side-stepped; each write command results in the data immediately being written, with no waiting for a subsequent command to fill up data to a full page. In the case of a CH32V30x with 256KB flash, even though the page size is larger (and thus would still require multiple commands to fill a page for writing), it could be up to a massive 586 fewer write (and verify!) commands (4682 vs 4096)! Assuming the '30x bootloader supports 64-byte chunks, that is. To finish, while I think this could be a worthwhile change, I do think there are some questions that need definite answers before such a modification is considered:
|
That would probably be a reasonable thing to do. I don't know much about the workings of the bootloader. |
I can answer one of my own questions now:
I managed to get hold of a dump of the bootloader from a CH32V30x and after some investigation it appears that it only allocates 64-byte buffers in RAM for storing both command and response payloads (i.e. all data not including serial header bytes and checksum). This is independent of transport in use (USB or UART). This means that command chunk size can only be a maximum of 61 bytes (64 minus 3 bytes overhead) on the CH32V30x, so my idea will unfortunately not be possible. 😞 Unless... we can make chunk size specified per device... 🤔 @Thom-de-Jong I am not aware of any documentation from WCH about how the bootloaders on CH32V chips work or the communications protocol. Not even in Chinese. The only things I've seen are guides on how to use their ISP software. I assume all the work for wchisp was done based on reverse-engineering WCHISPTool's behaviour. Yes, perhaps leave it for now and make a future PR if it turns out to be feasible to implement. |
Thanks. I'll check this later this week.🙏 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. It works like a charm.
Added a serial transport layer.
This required a little refactoring of the Flashing struct but nothing to serious.
Tested on a CH32V203C8T6 dev-board with FTDI FT232RL usb-to-serial adapter on windows and linux.