Parameter in constant array?

I’ve noticed that a constant can refer to a module parameter, but not an array that includes a module parameter.

module sample_test #(
    SAMPLE_SIZE=8d32
)(
    input clk,
    input rst
) {
    const A_CONSTANT = SAMPLE_SIZE	// this is ok

    const META_TEXT = $reverse (c{
	{8h02}, "test", {8d0},
	{8h40}, {SAMPLE_SIZE},		// compile error
	{8h40}, {SAMPLE_SIZE[7:0]},	// compile error
	{8h00}
    })
}
Issues detected in sample_test.luc:
    Error at line 9 offset 22: The value assigned to a constant must be constant!
    Error at line 9 offset 32: $reverse() can only be used on constant expressions or signals.

Does this have something to do with the lack of an upper bound for the module parameter width? It seems like the last reference should work at least, since I explicitly pull 8 bits.

@alchitry Is there something I am not understanding in terms of the syntax, or might this be a bug or emhancement, at least in the [7:0] case? Thanks for any guidance you can provide, when you get a chance.

Sorry for the delay and thanks for the tag to get me notified.

This looks like a bug to me. I’ll dig into it today and see what’s going on.

You’d need to explicitly size the parameter like you did with bit indexing or by using $resize(). Obviously, that isn’t working still.

I’m sure it has to do with how parameters are “known” values and not “constant” values in the backend. (Known values are constant but their value isn’t known until build time).

Fixed in Fixed issue with parameter widths and constant selections. Improved $… · alchitry/Alchitry-Labs-V2@60fdc3c · GitHub

It was also illegal to use $reverse() before on anything that wasn’t a constant or signal. I extended the functionality to allow for concatenations and array builders to be passed in.

I’ll be pushing 2.0.45 soon with the fix.

The fix does exactly what I was hoping. Thanks for the quick, helpful response.

@alchitry I think there might be a regression on this one in beta 2.0.48.

$ cat source/sample_test.luc
module sample_test #(
    SAMPLE_SIZE=8d32
)(
    input clk,
    input rst
) {
    const META_TEXT = $reverse (c{
	{8h40}, {SAMPLE_SIZE[7:0]}
    })
}

Works in 2.0.47:

$ /opt/alchitry-labs/alchitry-labs-2.0.47/bin/alchitry build $PWD/apple2c.alp
Issues detected in sample_test.luc:
    Warning at line 4 offset 10: The input "clk" is never read.
    Warning at line 5 offset 10: The input "rst" is never read.
    Warning at line 7 offset 10: The constant "META_TEXT" is never used.

Starting Vivado...

****** Vivado v2025.1 (64-bit)
... (compiles) ...

Fails validation in 2.0.48:

$ /opt/alchitry-labs/alchitry-labs-2.0.48/bin/alchitry build $PWD/apple2c.alp
Issues detected in sample_test.luc:
    Warning at line 4 offset 10: The input "clk" is never read.
    Warning at line 5 offset 10: The input "rst" is never read.
    Warning at line 7 offset 10: The constant "META_TEXT" is never used.

Failed to convert module "sample_test" to Verilog!
Failed to convert source files to Verilog. This should be considered a bug!
Undefined width during verilog conversion!
java.lang.IllegalStateException: Undefined width during verilog conversion!
	at com.alchitry.labs2.parsers.hdl.lucid.SystemVerilogConverter.toVerilog(SystemVerilogConverter.kt:862)
	at com.alchitry.labs2.parsers.hdl.lucid.SystemVerilogConverter.toVerilog(SystemVerilogConverter.kt:880)
	at com.alchitry.labs2.parsers.hdl.lucid.SystemVerilogConverter.exitSignal(SystemVerilogConverter.kt:907)
	at com.alchitry.labs2.parsers.grammar.LucidParser$SignalContext.exitRule(LucidParser.kt:5851)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.exitRule(ParseTreeMultiWalker.kt:111)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:46)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk(ParseTreeMultiWalker.kt:44)
	at com.alchitry.labs2.parsers.ParseTreeMultiWalker.walk$default(ParseTreeMultiWalker.kt:10)
	at com.alchitry.labs2.parsers.hdl.lucid.context.LucidBlockContext.walk(LucidBlockContext.kt:204)
	at com.alchitry.labs2.parsers.hdl.lucid.context.LucidBlockContext.walk$default(LucidBlockContext.kt:203)
	at com.alchitry.labs2.parsers.hdl.lucid.context.LucidBlockContext.convertToSystemVerilog(LucidBlockContext.kt:231)
	at com.alchitry.labs2.parsers.hdl.types.Module.convertToVerilog(Module.kt:28)
	at com.alchitry.labs2.parsers.ProjectContext.convertToVerilog(ProjectContext.kt:80)
	at com.alchitry.labs2.project.Project$build$2.invokeSuspend(Project.kt:326)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:586)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:829)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:717)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:704)

Issues detected in sample_test.luc:
    Warning at line 4 offset 10: The input "clk" is never read.
    Warning at line 5 offset 10: The input "rst" is never read.
    Warning at line 7 offset 10: The constant "META_TEXT" is never used.


Thanks for any help you can provide.

Well that turned out to be a trickier bug than I expected. It happened when fixing another bug when using $width() on a parameter. Parameters have undefined widths so they need to be handled specially. The old bug was using the width of the parameter’s current value so when that was fixed to make the widths undefined this new bug came up.

This bug is triggered when using signal selectors on parameters (it has nothing to do with $reverse). A workaround should be to use $resize() instead of the signal selector. This is also the safer option as using [7:0] will break if SAMPLE_SIZE is set to something like 10 (without a size prefix) but $resize will work. If you want to ensure the width is 8 bits, you should provide a constraint on SAMPLE_SIZE like $width(SAMPLE_SIZE) == 8.

I believe I fixed it in commit 9e41d85

I confirmed the fix in 2.0.49 with my old code, and I also changed my code with your $resize() suggestion to make my own code more resilient. Thanks again.