Hello,
I'm not sure how to make a suggestion about the compiler so I'll post this idea here.
When I do something like the following outside of a function:
let
task1Name: cstring = "task 1"
I think it would make sense for the compiler to translate that to:
const NCSTRING task1Name_u8aig98 = "task 1"
Instead, the variable is declared without the const qualification and the initial value is copied in during the Nim runtime startup function. This doesn't make much of a difference for PC based programs as the program is copied to and then run from RAM anyways, but for embedded targets, that const qualification is the difference of the value being referenced by the linker from flash or not. With the current way, for a cstring, extra space is required. Once when the string "task 1" is placed into flash and then room for another pointer to the string is reserved in RAM as seen from the listing generated by msp430-gcc.
00002402 g O .bss 00000002 task1Name_GUVMzHshlbCKkNShwK809bA
My suggestion is that variables that are "letted" outside of a function scope in the module's global scope generate code like the Nim code below by default.
var
task1Name{.codegenDecl: "const $1 $2 = \"task1\"".}: cstring
Listing:
00008006 g O .rodata 00000002 task1Name_GUVMzHshlbCKkNShwK809bA
This would also imply that structures, or objects, could be created statically in the read only section. Currently I can't make that be the case.
These issues make Nim totally unsuited for embedded work or anything on a microcontroller like the site claims in its current form.
const
task1Name: cstring = "task 1"
Gives what you need, there are specific reasons why let variables are not read only.
Const as far as I can tell, is the equivalent to a text substitution macro in the generated c. That's what the generated c looks like.
Perhaps I'm using it wrong?
Nim let is not equivalent to const in C. if you don't understand why read https://nim-lang.org/docs/manual.html
Hint: ref objects
Const as far as I can tell, is the equivalent to a text substitution macro in the generated c. >That's what the generated c looks like.
I guessed that.
Is that a problem? You may fear increase of code size or memory consumption, but I think gcc's memory pooling catches that. Have you inspected assembly code?
I do understand how ref objects make that not a universally applicable proposition.
The issue is that the type declaration syntax I did above does a heap allocation (I think, based on the error that shows up because Nims memory manager was written with the assumption of at least a 32bit memory addressing capability) even if the type is declared as an object instead of a ref object. This was surprising to me.
Gcc's string pooling does take care of it. I didn't know it was called string pooling so thanks for the vocab!
Something that must be done in embedded is passing a pointer to a struct that is stored in flash. I can't figure out how to do that.
Instead of speaking out of frustration, I should have asked directly how to get a pointer to an object that is in the rodata section and how to put it there in the first place.
I've seen many posts on the internet where someone asks about Nim for embedded and Dom asks what is missing. The person never replies to Dom's question. This kind of memory control is critical for embedded. If it is possible without jumping through hoops like too many codegenDecl pragmas, some examples in the manual about placing data would go a long, long way to inspiring interest for embedded work.
I understand the thrust now is 1.0. That is great and will hopefully bring more people, but after that, more support or documentation for embedded stuff would be great. Embedded really, really could use an improved C. There is no competitor to C in that space right now. None! Nim could make a dent there. I'm not counting embedded Linux as embedded work either. RTOS or bare metal is what I'm getting at.
some examples in the manual about placing data would go a long, long way to inspiring interest for embedded work.
Agree that we need more docs about this but I don't think it manual is the right place. Please consider writing guest posts like this user has done for nim-lang.org, or better yet setting up a dedicated site for embedded docs (I can even point embedded.nim-lang.org at it if you'd like).
As an aside I think people's replies to your thread are a bit too blunt. I think your suggestion of how let should work triggered some people :)
Embedded really, really could use an improved C. There is no competitor to C in that space right now. None! Nim could make a dent there.
Isn't Rust hailed as the great alternative to C in this domain?
I'm not a rust enthusiast and haven't spent any time with it besides reading some blog posts. From the looks of it, it appears further along than Nim, but is not production ready. It also seems to encourage abstraction to the point that BOM costs could be higher due to increased memory requirements?
Again, not a rust expert or even enthusiast, but Nim seemed more promising to me which is why I started here on my quest for a better C. (actually I guess started with C++)
Yes the compiler can merge string literals. Please show me how to make a non-zeroed struct appear in the rodata section of an elf file.
After talking with a co-worker, he thinks (and I'm not sure) that putting const in front of a global variable definition doesn't guarantee it ends up in rodata section (and thus flash memory), but it is certainly at least a convention that all embedded MCU targeting compilers follow in my experience. IAR then has further attributes like __data16 but either way, the struct definition needs to take place entirely outside of a function. You can't just write to flash on a whim during the Nim runtime startup.
Do you know how to do that with os:standalone and gc:none?
@jba The code
let myVar = myType(parm1: 4)
will only perform heap allocation if myType is a ref object type. If it's an object type, it should be allocated on the stack.
# main.nim
type
myType = object
val: int
var myName: myType = myType(val: 2)
# panicoverride.nim
{.push stack_trace: off, profiler:off.}
proc rawoutput(s: string) = discard
proc panic(s: string) = discard
{.pop.}
Compile this with: !nim c -d:release --compileOnly --opt:size --os:standalone --gc:none main
I get on my 64 bit linux machine with Nim 0.18.0 the following error message:
Hint: used config file '/etc/nim.cfg' [Conf]
Hint: system [Processing]
Hint: main [Processing]
Error: system module needs 'genericReset'
The '/etc/nim.cfg' is unchanged from the one that came with Nim.
With just --os:standalone I get "needs 'echoBinSafe'"
With --os:standalone and --cpu:msp430 I get:
Hint: used config file '/etc/nim.cfg' [Conf]
Hint: system [Processing]
lib/nim/system/alloc.nim(136, 16) Error: type mismatch: got <uint32, int32>
but expected one of:
proc `<=`(x, y: pointer): bool
first type mismatch at position: 1
required type: pointer
but expression 'x' is of type: uint32
proc `<=`(x, y: bool): bool
first type mismatch at position: 1
required type: bool
but expression 'x' is of type: uint32
proc `<=`(x, y: string): bool
first type mismatch at position: 1
required type: string
but expression 'x' is of type: uint32
proc `<=`[Enum: enum](x, y: Enum): bool
first type mismatch at position: 1
required type: Enum: enum
but expression 'x' is of type: uint32
proc `<=`[T: tuple](x, y: T): bool
first type mismatch at position: 1
required type: T: tuple
but expression 'x' is of type: uint32
proc `<=`[T](x, y: ref T): bool
first type mismatch at position: 1
required type: ref T
but expression 'x' is of type: uint32
proc `<=`(x, y: int32): bool
first type mismatch at position: 1
required type: int32
but expression 'x' is of type: uint32
proc `<=`[T: SomeUnsignedInt](x, y: T): bool
first type mismatch at position: 2
required type: T: SomeUnsignedInt
but expression '0x0000FFFF' is of type: int32
proc `<=`(x, y: int): bool
first type mismatch at position: 1
required type: int
but expression 'x' is of type: uint32
proc `<=`[T](x, y: set[T]): bool
first type mismatch at position: 1
required type: set[T]
but expression 'x' is of type: uint32
proc `<=`(x, y: float): bool
first type mismatch at position: 1
required type: float
but expression 'x' is of type: uint32
proc `<=`(x, y: char): bool
first type mismatch at position: 1
required type: char
but expression 'x' is of type: uint32
proc `<=`(x, y: int16): bool
first type mismatch at position: 1
required type: int16
but expression 'x' is of type: uint32
proc `<=`(x, y: int64): bool
first type mismatch at position: 1
required type: int64
but expression 'x' is of type: uint32
proc `<=`(x, y: float32): bool
first type mismatch at position: 1
required type: float32
but expression 'x' is of type: uint32
proc `<=`(x, y: int8): bool
first type mismatch at position: 1
required type: int8
but expression 'x' is of type: uint32
expression: x <= 0x0000FFFF
The alloc error message you had told me before was due to the 16 bit integer width and unavoidable with the current implementation. It seems like there is dynamic memory allocation based on the name.
And further, since I have your ear and no one else has provided a solution, how do I get a struct definition to appear in the rodata section of the elf file. As far as I know, this would require a struct to be fully defined in the global scope of a c file (along with either const or some compiler specific intrinsic.)