In Spin, "global" variables (the ones in the VAR sections) should be initialized to zero. However, if I run the attached code it prints out
Cog0 init head = $0, tail = $5, used = $5
Cog0 zero head = $0, tail = $0, used = $0
which means that the variables tail and used are NOT initialized to zero. As none of my code is executed before Demo() I think it's not my fault. Somehow the memory for the VAR section must have been overwritten with something else. If I change the code only a slight bit (different length of the strings or removing one of the debug() statements, for example) the contents of the variables also change. BTW, after initializing the variables to zero manually, the rest of the program works as expected.
It gets even funnier if I call functions of DebugLog.spin2 from C code instead of compiling it stand-alone. I get error messages like
C:/flexprop/include/libc/stdio/fprintf.c:23: error: unknown identifier putchar in class _struct___fmtfile
C:/flexprop/include/libc/stdio/fprintf.c:23: error: putchar is not a member of _struct___fmtfile
I guess that the OBJ c: "libc.spin2" trick works for Spin only programs but creates some name space conflicts if C and Spin are mixed together.
@ManAtWork When I run your DebugLog.spin2 program stand-alone it prints:
( Entering terminal mode. Press Ctrl-] or Ctrl-Z to exit. )
Cog0 INIT $0000_0000 $0000_0000 load
Cog0 INIT $0000_0404 $0000_0000 load
Cog0 init head = $0, tail = $0, used = $0
Cog0 zero head = $0, tail = $0, used = $0
Cog0 Alloc head = $0, tail = $0, used = $0
Cog0 exit head = $0, tail = $0, used = $2
Cog0 Alloc head = $2, tail = $0, used = $2
Cog0 exit head = $2, tail = $0, used = $5
which I think is expected. Are you using loadp2 to run your program, or have you put it in flash? There might be some kind of startup problem when booting from flash, perhaps the boot code in ROM does not zero memory in this case.
As for using DebugLog.spin2 from a C program, you'll end up with two copies of the C library (the normal one plus the one loaded by libc.spin2 inside DebugLog.spin2) and that almost certainly won't work, even if you could get it to compile.
Eric,
Is it the case that mount() does not action the mounting process until a file is referenced?
I've been trying to detail all steps of my testing code but am getting tripped up by the mount() function. It doesn't seem to do anything until a subsequent function wants to access the mounted volume. errno isn't being set by mount(), and also if I enable debugging in sdmm.c the init code in there is not called until a subsequent file access is made.
Mount() typically does do some things when it is called (it depends on the file system) but not much in the way of error checking is performed. I can't recall details of the fat32 file system, but it's certainly possible that it defers some actions until a file is actually opened.
Oh yes the error codes. I usually just get "unknown error" and "I/O Error" though. Btw @evanh, sadly it got cut in the recording, but last time on Live Forum, we found out that on Chip's SD card, the driver seems to fail when running at 320+ MHz. Whatever shell.c that comes with flexprop is using works fine though.
@Wuerfel_21 said:
but last time on Live Forum, we found out that on Chip's SD card, the driver seems to fail when running at 320+ MHz.
I prolly need to back off the data rate a little more. Realistically, that inline resistor that Parallax puts in there limits the bit rate to 25 MHz. It gets unreliable above that. I tried to reach 50 MHz but it never worked. I ended up with ~40 MHz working on my Eval board and I tested it at high temperature too ... but there wasn't any leeway left. I even intentionally made the clock ratio lopsided to assist.
I have this 25 MHz selection as an alternative sitting in the Standard Capacity code path but it wasn't ever tested. You can see the clock ratios are all an even 50%.
Just run speed tests using the slower 25 MHz SPI clock option. Room temp works up to 370 MHz sysclock. Looks like I had tested it previously too, since there is a little more to that code. I've now moved the header comment to the head of that code.
EDIT: 360 MHz fails above 5045 oC. 350 MHz fails above 60 oC. 340 MHz fails above 70 oC. 330 MHz fails above 85 oC.
EDIT2: 380 MHz and 390 MHz works below room temperature. Everything above 350 MHz is also exceeding the 25 MHz SPI clock.
EDIT3: Oops, I've updated sdmm.cc again, debug turned off now. Also removed the sysclock/14 divider step. Highest divider now sysclock/12. Eg: 360 MHz sysclock = 30 MHz SPI clock. 30 MHz has tested as reliable as 25 MHz.
EDIT4: And a further update. This one is adding in the earlier support code written for operating Roger Loh's 4-bit SD Eval Add-on Board. The driver is still just operating in 1-bit SPI mode but the pin order resulting from the 4-bit pin arrangement demands extra code to handle configuring the rx smartpin at greater than three spacing between clock and data pins. Luckily Roger has the tx data pin close to the clock pin, otherwise smartpins wouldn't be usable with his add-on board.
@Wuerfel_21 said:
but last time on Live Forum, we found out that on Chip's SD card, the driver seems to fail when running at 320+ MHz.
I prolly need to back off the data rate a little more. Realistically, that inline resistor that Parallax puts in there limits the bit rate to 25 MHz. It gets unreliable above that. I tried to reach 50 MHz but it never worked. I ended up with ~40 MHz working on my Eval board and I tested it at high temperature too ... but there wasn't any leeway left. I even intentionally made the clock ratio lopsided to assist.
My EC32 with Sandisk card (tried several different cards from 32 to 128 GB) works with sysclock/8 at 340 MHz, is unstable at sysclock/7 and doesn't work at sysclock/6
Yes. C doesn't care about whitespace. This is why you should always put the * on the right side, i.e. int *x, y;: it better reflects how the compiler actually parses things: an int *x; and an int y;.
C is made much more difficult when people do wrong stylistic things like int* x; (and typedef struct { ... } name; rather than plain struct name { ... };), because writing things that way is subtly inconsistent with how the language really works. This isn't your fault; you probably picked it up from one or more of the many C books and tutorials that recommended doing things the wrong way because it's "easier", even though it results in a bad understanding that ends in frustration.
I used to do most of these misleading things in my own C. Once I stopped, and started writing C the same way the compiler parses C, everything clicked and I quickly came to feel that I actually understood the language.
New niggle found in Flexspin. I set up an atexit(cleanup) callback in my C code and it works when I use any exit() function but not when main() finishes.
Also, opendir() doesn't seem to set errno upon success.
PS: The sdmm.cc file is taken from build of master branch on 2 Mar 2024, version 6.9.0-beta-v6.8.1-15-g4b3de21a. A quick git pull check shows no changes to this file since then.
@evanh said:
New niggle found in Flexspin. I set up an atexit(cleanup) callback in my C code and it works when I use any exit() function but not when main() finishes.
I think you actually need to give flexspin the -x command line option (to make error codes significant) in order for atexit() to be called. That is probably a bug, but there's some overhead to all the exit handlers and I wanted to avoid that by default.
Also, opendir() doesn't seem to set errno upon success.
That's actually working as intended. From the Linux errno man page:
errno
The value in errno is significant only when the return value of the
call indicated an error (i.e., -1 from most system calls; -1 or NULL
from most library functions); a function that succeeds is allowed to
change errno. The value of errno is never set to zero by any system
call or library function.
Eric,
I'm at a loss as to why Flexspin is warning me about a parameter count. I've updated all three cases of it that I could find. The warning message I'm getting is: ...include/filesys/sdfatfs/fatfs_vfs.c:31: warning: Bad number of parameters in call to disk_setpins: expected 5 found 4.
First up, I've duplicated the entirety of include/filesys/fatfs/* and named the new directory sdfatfs. The original setpins function has five parameters but I want only four in the new edition.
The three places I found and updated disk_setpins() is:
File include/filesys/sdfatfs/fatfs_vfs.c
r = FFS->disk_setpins(drv, pclk, pcmd, pdat);
File include/filesys/sdfatfs/diskio.h
DRESULT disk_setpins (BYTE pdrv, int pclk, int pcmd, int pdat) _IMPL("sd4b.cc");
File include/filesys/sdfatfs/sd4b.cc (renamed from sdmm.cc)
DRESULT disk_setpins(int drv, int pclk, int pcmd, int pdat)
Also, in file include/sys/vfs.h I added a fresh entry:
struct vfs *_vfs_open_sdcard4b(int pclk = 21, int pcmd = 20, int pdat = 16) _IMPL("filesys/sdfatfs/fatfs_vfs.c");
Attached is the entire duplicated directory as it stands. The new driver routines are not in place, I'm only trying to organise the wrapper for them at this stage.
@evanh : I couldn't compile the code you posted, it was missing a bunch of defines in sd4b.cc (like CS_INIT, CS_H, CK_L, and so on). After copying those from sdmm.cc and a bit of fiddling I was able to compile a test program, and it built fine. So I'm not sure where your "Bad number of parameters" error is coming from. However, my test program only tried to mount the one file system. Maybe having both file systems at the same time is causing a conflict? (Nope, I just added another mount and it worked fine.)
Oh, sorry, I should have given you my test program too. As for our different results, now you mention it I didn't get any errors and, you're right, I should have expect to given how much I'd ripped things up. I'll have another go tonight ...
@Wuerfel_21 said:
Oh yes the error codes. I usually just get "unknown error" and "I/O Error" though. Btw @evanh, sadly it got cut in the recording, but last time on Live Forum, we found out that on Chip's SD card, the driver seems to fail when running at 320+ MHz. Whatever shell.c that comes with flexprop is using works fine though.
Oh regarding this: Even with the slower version it doesn't work. We tried going down to sysclk/16 and it still wouldn't properly mount it. So that was never the issue, I think that change can be reverted. Possibly some clkfreq-dependent delay somewhere?
@Wuerfel_21 said:
... Possibly some clkfreq-dependent delay somewhere?
Damn! I guess, yeah. It'd have to be with a specific make/model of SD card then.
PS: Don't revert the changes. There isn't a huge different in performance. I feel a lot happier with it a little more conservative now. That inline resistor needs respecting.
@Wuerfel_21 said:
... Whatever shell.c that comes with flexprop is using works fine though.
It mounts the same way as normal: mount("/sd", _vfs_open_sdcard());
The only thing unique there is it also mounts the Plan9 FS over the comport ... and maybe the use of chdir(arg1);
EDIT: Ah, it uses fgetc()/fputc() for the copying, rather than fread()/fwrite(). That'll be a lot slower I suspect. And only uses single block read/write SD commands.
EDIT2: Another rule that is being readily broken is the 400 kHz clock frequency during SD card init. I'm just doing a bland sysclock/16, which can be up to 20 MHz SPI clock. It doesn't seem to hurt and in this case can't be the issue given the shell.c program works fine with the same init code.
There's a tiny bug in there when using Roger's 4-bit add-on board, which I think only me and him have one of. The de-init routine fails to clear the MISO pin mode because that pin is no longer the rx smartpin. Fixed it in this update.
Also updated the debug reporting of SPI clock rate. Instead of printing a fixed number for a given code path, it now calculates the actual SPI clock rate in use.
And another round ... T'was going through cleaning out the 4-bit driver code and realised there is improvement to the way I'm handling the remapping of smarpin/clock/rx to suitable pins. So I've gone back and improved it in sdmm.cc as well. In the process also added a check and out of range error when the clock and data pins are too far apart. Applies to both tx and rx smartpins. Something that should have been in place from the start.
However, the way the libs are handling a failed init don't seem all that orderly. The opendir() I was using for checking for card presence doesn't report any error. Not until I try to open a file for writing does it decide to complain with a permission denied.Oops, wrong program. It's locking up on opendir() when the pins are too far apart ...
Anyhow, here's the latest sdmm.cc source file. And the test program that locks up - I've made the MOSI pin too far from CLK pin.
EDIT2: Bah! I've fixed the lock up. Incorrectly used the return code as the driver status. Now it's back to an Unknown error on opendir(). So good enough.
Comments
In Spin, "global" variables (the ones in the VAR sections) should be initialized to zero. However, if I run the attached code it prints out
which means that the variables tail and used are NOT initialized to zero. As none of my code is executed before Demo() I think it's not my fault. Somehow the memory for the VAR section must have been overwritten with something else. If I change the code only a slight bit (different length of the strings or removing one of the debug() statements, for example) the contents of the variables also change. BTW, after initializing the variables to zero manually, the rest of the program works as expected.
It gets even funnier if I call functions of DebugLog.spin2 from C code instead of compiling it stand-alone. I get error messages like
I guess that the
OBJ c: "libc.spin2"
trick works for Spin only programs but creates some name space conflicts if C and Spin are mixed together.@ManAtWork When I run your DebugLog.spin2 program stand-alone it prints:
which I think is expected. Are you using loadp2 to run your program, or have you put it in flash? There might be some kind of startup problem when booting from flash, perhaps the boot code in ROM does not zero memory in this case.
As for using DebugLog.spin2 from a C program, you'll end up with two copies of the C library (the normal one plus the one loaded by libc.spin2 inside DebugLog.spin2) and that almost certainly won't work, even if you could get it to compile.
Eric,
Is it the case that
mount()
does not action the mounting process until a file is referenced?I've been trying to detail all steps of my testing code but am getting tripped up by the mount() function. It doesn't seem to do anything until a subsequent function wants to access the mounted volume.
errno
isn't being set by mount(), and also if I enable debugging insdmm.c
the init code in there is not called until a subsequent file access is made.Mount() typically does do some things when it is called (it depends on the file system) but not much in the way of error checking is performed. I can't recall details of the fat32 file system, but it's certainly possible that it defers some actions until a file is actually opened.
Okay, next problem. I'd like to improve the error codes. I've found there is no distinction between media and no media present.
eg: When there is no SD card inserted I get this when trying to delete a file:
Then when there is a card inserted and it has a matching file to delete and I attempt to delete it twice I get this:
Oh yes the error codes. I usually just get "unknown error" and "I/O Error" though. Btw @evanh, sadly it got cut in the recording, but last time on Live Forum, we found out that on Chip's SD card, the driver seems to fail when running at 320+ MHz. Whatever shell.c that comes with flexprop is using works fine though.
I prolly need to back off the data rate a little more. Realistically, that inline resistor that Parallax puts in there limits the bit rate to 25 MHz. It gets unreliable above that. I tried to reach 50 MHz but it never worked. I ended up with ~40 MHz working on my Eval board and I tested it at high temperature too ... but there wasn't any leeway left. I even intentionally made the clock ratio lopsided to assist.
Here's the code that selects the divider:
I have this 25 MHz selection as an alternative sitting in the Standard Capacity code path but it wasn't ever tested. You can see the clock ratios are all an even 50%.
Just run speed tests using the slower 25 MHz SPI clock option. Room temp works up to 370 MHz sysclock. Looks like I had tested it previously too, since there is a little more to that code. I've now moved the header comment to the head of that code.
Updated sdmm.cc attached.
EDIT: 360 MHz fails above 5045 oC. 350 MHz fails above 60 oC. 340 MHz fails above 70 oC. 330 MHz fails above 85 oC.
EDIT2: 380 MHz and 390 MHz works below room temperature. Everything above 350 MHz is also exceeding the 25 MHz SPI clock.
EDIT3: Oops, I've updated sdmm.cc again, debug turned off now. Also removed the sysclock/14 divider step. Highest divider now sysclock/12. Eg: 360 MHz sysclock = 30 MHz SPI clock. 30 MHz has tested as reliable as 25 MHz.
EDIT4: And a further update. This one is adding in the earlier support code written for operating Roger Loh's 4-bit SD Eval Add-on Board. The driver is still just operating in 1-bit SPI mode but the pin order resulting from the 4-bit pin arrangement demands extra code to handle configuring the rx smartpin at greater than three spacing between clock and data pins. Luckily Roger has the tx data pin close to the clock pin, otherwise smartpins wouldn't be usable with his add-on board.
In both cases? > @evanh said:
My EC32 with Sandisk card (tried several different cards from 32 to 128 GB) works with sysclock/8 at 340 MHz, is unstable at sysclock/7 and doesn't work at sysclock/6
Yes. C doesn't care about whitespace. This is why you should always put the
*
on the right side, i.e.int *x, y;
: it better reflects how the compiler actually parses things: anint *x;
and anint y;
.C is made much more difficult when people do wrong stylistic things like
int* x;
(andtypedef struct { ... } name;
rather than plainstruct name { ... };
), because writing things that way is subtly inconsistent with how the language really works. This isn't your fault; you probably picked it up from one or more of the many C books and tutorials that recommended doing things the wrong way because it's "easier", even though it results in a bad understanding that ends in frustration.I used to do most of these misleading things in my own C. Once I stopped, and started writing C the same way the compiler parses C, everything clicked and I quickly came to feel that I actually understood the language.
The Linux kernel C style guide is the best online style guide I've found that doesn't recommend any of these bad things that go against the grain of the language: https://www.kernel.org/doc/html/latest/process/coding-style.html
New niggle found in Flexspin. I set up an
atexit(cleanup)
callback in my C code and it works when I use any exit() function but not when main() finishes.Also,
opendir()
doesn't seem to seterrno
upon success.I've updated sdmm.cc again - https://forums.parallax.com/discussion/comment/1557945/#Comment_1557945
EDIT: And again.
PS: The sdmm.cc file is taken from build of master branch on 2 Mar 2024, version 6.9.0-beta-v6.8.1-15-g4b3de21a. A quick
git pull
check shows no changes to this file since then.I think you actually need to give flexspin the -x command line option (to make error codes significant) in order for atexit() to be called. That is probably a bug, but there's some overhead to all the exit handlers and I wanted to avoid that by default.
That's actually working as intended. From the Linux errno man page:
Thanks for the sdmm update!
Oh, okay. I've since worked out I can write to errno. So I guess clearing it first is considered legit practice then.
Eric,
I'm at a loss as to why Flexspin is warning me about a parameter count. I've updated all three cases of it that I could find. The warning message I'm getting is: ...
include/filesys/sdfatfs/fatfs_vfs.c:31: warning: Bad number of parameters in call to disk_setpins: expected 5 found 4
.First up, I've duplicated the entirety of
include/filesys/fatfs/*
and named the new directorysdfatfs
. The original setpins function has five parameters but I want only four in the new edition.The three places I found and updated
disk_setpins()
is:include/filesys/sdfatfs/fatfs_vfs.c
include/filesys/sdfatfs/diskio.h
include/filesys/sdfatfs/sd4b.cc
(renamed from sdmm.cc)Also, in file
include/sys/vfs.h
I added a fresh entry:Attached is the entire duplicated directory as it stands. The new driver routines are not in place, I'm only trying to organise the wrapper for them at this stage.
@evanh : I couldn't compile the code you posted, it was missing a bunch of defines in sd4b.cc (like CS_INIT, CS_H, CK_L, and so on). After copying those from sdmm.cc and a bit of fiddling I was able to compile a test program, and it built fine. So I'm not sure where your "Bad number of parameters" error is coming from. However, my test program only tried to mount the one file system. Maybe having both file systems at the same time is causing a conflict? (Nope, I just added another mount and it worked fine.)
Oh, sorry, I should have given you my test program too. As for our different results, now you mention it I didn't get any errors and, you're right, I should have expect to given how much I'd ripped things up. I'll have another go tonight ...
Oh regarding this: Even with the slower version it doesn't work. We tried going down to sysclk/16 and it still wouldn't properly mount it. So that was never the issue, I think that change can be reverted. Possibly some clkfreq-dependent delay somewhere?
Damn! I guess, yeah. It'd have to be with a specific make/model of SD card then.
PS: Don't revert the changes. There isn't a huge different in performance. I feel a lot happier with it a little more conservative now. That inline resistor needs respecting.
I find there is. It should at the very least be an option that isn't buried in editing the include files.
It mounts the same way as normal:
mount("/sd", _vfs_open_sdcard());
The only thing unique there is it also mounts the Plan9 FS over the comport ... and maybe the use of
chdir(arg1);
EDIT: Ah, it uses fgetc()/fputc() for the copying, rather than fread()/fwrite(). That'll be a lot slower I suspect. And only uses single block read/write SD commands.
EDIT2: Another rule that is being readily broken is the 400 kHz clock frequency during SD card init. I'm just doing a bland sysclock/16, which can be up to 20 MHz SPI clock. It doesn't seem to hurt and in this case can't be the issue given the shell.c program works fine with the same init code.
How would that be made an option?
The fatfs already has a lot if its options controlled by -D switches
So I can simply add in any old
#ifdef <label>
I choose, and that's command-able from the one compile?Oh, I had wondered where the _DEBUG_SDMM switch was supposed to be set. I kept adding it in the source file!
Yeah, that's how it works.
Done. Switch is
_SDHC_45MHZ
There's a tiny bug in there when using Roger's 4-bit add-on board, which I think only me and him have one of. The de-init routine fails to clear the MISO pin mode because that pin is no longer the rx smartpin. Fixed it in this update.
Also updated the debug reporting of SPI clock rate. Instead of printing a fixed number for a given code path, it now calculates the actual SPI clock rate in use.
And another round ... T'was going through cleaning out the 4-bit driver code and realised there is improvement to the way I'm handling the remapping of smarpin/clock/rx to suitable pins. So I've gone back and improved it in sdmm.cc as well. In the process also added a check and out of range error when the clock and data pins are too far apart. Applies to both tx and rx smartpins. Something that should have been in place from the start.
However, the way the libs are handling a failed init don't seem all that orderly. The
opendir()
I was using for checking for card presence doesn't report any error. Not until I try to open a file for writing does it decide to complain with apermission denied
. Oops, wrong program. It's locking up on opendir() when the pins are too far apart ...Anyhow, here's the latest sdmm.cc source file. And the test program that locks up - I've made the MOSI pin too far from CLK pin.
EDIT2: Bah! I've fixed the lock up. Incorrectly used the return code as the driver status. Now it's back to an
Unknown error
on opendir(). So good enough.