What is the right way to determine, in CGo, if the environment supports AVX-512? - x86-64

CGo appears to be removing preprocessor definitions. Has anyone else run into this?
In my other C code on the same machine, this works fine. In CGo, the "__AVX2__" definition gets removed.
I've tried this with and without the '-march=native'
How can I get it back ?
The code:
//go:build amd64 && !purego && gc
// +build amd64,!purego,gc
// #cgo CFLAGS: -march=native
package keccakAVX512
/*
// check if the amd64 machine supports AVX512 instructions at build time and call
// an assembly function using AVX512 if so.
#ifdef __AVX2__
void keccakF1600_AVX512(unsigned long* state);
this should fail with a syntax error but doesn't
#endif
// void keccakF1600_AVX512(unsigned long* state) {};
*/
import "C"
func KeccakF1600AMDAVX512(a *[25]uint64) {
C.keccakF1600_AVX512((*C.ulong)(&a[0]))
}
With the 'go tool cgo' command, I see that the default '-m64' is taking precedence, even with the CFLAGS set.

I found that the problem was in the #cgo syntax. It has to be inside the '/**/' comment block instead of the '//' comment block.
package keccakAVX512
/*
#cgo CFLAGS: -march=native
// check if the amd64 machine supports AVX512 instructions at build time and call
// an assembly function using AVX512 if so.
#ifdef __AVX2__
void keccakF1600_AVX512(unsigned long* state);
#else
void keccakF1600_AVX512(unsigned long* state) {};
#endif
*/
import "C"
func KeccakF1600AMDAVX512(a *[25]uint64) {
C.keccakF1600_AVX512((*C.ulong)(&a[0]))
}

Related

getting "undefined reference to ledc_cb_register" error

I'm trying to use a callback function with the led controller of esp32, however I'm unable to compile the code. I'm not sure if something is missing or the code has errors, as I have a limited understanding of pointers or coding in general.
I'm using the Arduino framework, however when I hover over the ledc_cb_register text, VSCode will popup some more details/definition of this function, so I would expect that it does see the reference to it.
relevant esp32 documentation:
docs.espressif.com
I'm trying to copy the following example, but make it a bit simpler (using only one channel):
github
It seems this example can be compiled on my side too, but this uses espidf framework.
trying the following code (many lines are not shown here for simplicity)
static bool cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *user_arg)
{
portBASE_TYPE taskAwoken = pdFALSE;
if (param->event == LEDC_FADE_END_EVT) {
isFading = false;
}
return (taskAwoken == pdTRUE);
}
[...]
void setup() {
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_HIGH_SPEED_MODE, // timer mode
.duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
.timer_num = LEDC_TIMER_0, // timer index
.freq_hz = LED_frequency, // frequency of PWM signal
.clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = {
.gpio_num = LED_PIN,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.duty = 4000,
.hpoint = 0,
//.flags.output_invert = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
ledc_fade_func_install(0);
ledc_cbs_t callbacks = {
.fade_cb = cb_ledc_fade_end_event
};
ledc_cb_register(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, &callbacks, 0);
and getting the following error message:
[..]/.platformio/packages/toolchain-xtensa-esp32#8.4.0+2021r2-patch3/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\esp32dev\src\main.cpp.o:(.literal._Z5setupv+0x78): undefined reference to 'ledc_cb_register(ledc_mode_t, ledc_channel_t, ledc_cbs_t*, void*)'
[..]/.platformio/packages/toolchain-xtensa-esp32#8.4.0+2021r2-patch3/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\esp32dev\src\main.cpp.o: in function 'setup()':
[..]\PlatformIO\Projects\asdf/src/main.cpp:272: undefined reference to 'ledc_cb_register(ledc_mode_t, ledc_channel_t, ledc_cbs_t*, void*)'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\esp32dev\firmware.elf] Error 1
According to the docs, it seems to be a feature that was added in v4.4.4
but the latest Arduino core (2.0.6) is build on v4.4.3.
If you are not on the latest Arduino core, try updating that first and see if it works. If not, then you just have to wait until the Arduino core is updated to use ESP IDF v4.4.4.
Of course, you can use ledc_isr_register(...) to register an ISR handler for the interrupt.
Best of luck!
Update:
I realized that the problem (at least on my side when testing it) was that there is an error in the ledc.h file, where they forgot to add ledc_cb_register in an extern "C" block.
I manually patched it by moving the
#ifdef __cplusplus
}
#endif
part, which was located after the ledc_set_fade_step_and_start function, below ledc_cb_register instead.
So, the end of my ledc.h file looks like this now:
...
esp_err_t ledc_set_fade_step_and_start(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t target_duty, uint32_t scale, uint32_t cycle_num, ledc_fade_mode_t fade_mode);
/**
* #brief LEDC callback registration function
* ...
*/
esp_err_t ledc_cb_register(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_cbs_t *cbs, void *user_arg);
#ifdef __cplusplus
}
#endif

How can I unit test a specific DML method?

I'm writing some common DML code that contains a fairly complex method, something like:
saved uint32 checksum_ini;
method calculate_checksum(bytes_t data) -> (uint32 sum) {
uint32 result = checksum_ini;
for (int i = 0; i < data.size; ++i) {
result = f(result, data.data[i]);
}
return result;
}
My device calls the function indirectly by reading and writing some registers, which makes it cumbersome to unit test all corner cases of the checksum algorithm.
How can I efficiently write a unit test for my checksum implementation?
One approach is to create a dedicated test module, say test-checksum, containing a test device, say test_checksum_dev, that imports only your common code, and exposes the calculate_checksum method to Python, where it is easy to write tests. This is done in two steps: First, expose the method to C:
dml 1.4;
device test_checksum_dev;
import "checksum-common.dml";
// Make DML method calculate_checksum available as extern C symbol "calculate_checksum"
// The signature will be:
// uint64 calculate_checksum(conf_object_t *obj, bytes_t data)
export calculate_checksum as "calculate_checksum";
The second step is to expose it to Python. Create checksum.h:
#ifndef CHECKSUM_H
#define CHECKSUM_H
#include <simics/base/types.h>
#include <simics/pywrap.h>
extern uint32 calculate_checksum(conf_object_t *obj, bytes_t data);
#endif /* CHECKSUM_H */
(if you also add header %{ #include "checksum.h" %} to the DML file, you will get a hard check that signatures stay consistent).
Now add the header file to IFACE_FILES in your module makefile to create a Python wrapping:
SRC_FILES = test-checksum.dml
IFACE_FILES = checksum.h
include $(MODULE_MAKEFILE)
You can now call the DML method directly from your test:
SIM_load_module('test-checksum')
from simmod.test_checksum.checksum import calculate_checksum
obj = SIM_create_object('test_checksum_dev', 'dev', checksum_ini=0xdeadbeef)
assert calculate_checksum(obj, b'hello world') == (0xda39ba47).to_bytes(4, 'little')

Determine that Swift code is being run under Terminal

I'm working on implementation of a swift package with an adaptive logger that can determine environment for output purposes. The logger supports all platforms (macOS, iOS, tvOS etc.) and can be used from Tests or Swift command line apps as well.
My question: how to determine that the Swift code is being run under Terminal app for instance when run swift test?
I've found that ProcessInfo's environment property has a "_" field with "/usr/bin/swift" or "/Users/.../TestApp" values when you use the Terminal app but I'm not sure that is a correct approach.
var isTerminal : Bool {
return ProcessInfo.processInfo.environment["_"] != nil
}
Are there any other approaches to check this?
Instead of trying to detect whether you're using Terminal.app (which could be error-prone if you're e.g. using a different terminal program or running on Linux), you should instead query the terminal to find out whether it supports specific features. This can be done easily with the ncurses library.
1. Add a CCurses target to your Swift package
We need to create a system library target so that the Swift Package Manager knows how to find and link against the ncurses library Add the target to your Package.swift:
let package = Package(
// ...
targets: [
// ...
.systemLibrary(name: "CCurses"),
],
// ...
)
And create a new directory at Sources/CCurses with the following files:
curses.h
#include <curses.h>
#include <term.h>
module.modulemap
module CCurses [system] {
header "curses.h"
link "curses"
export *
}
2. Check if the terminal supports color output
When you're setting up, query the terminfo database to check for color support:
import CCurses
// ...
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
Based on https://stackoverflow.com/a/7426284.
If you need to check for the existence of another feature, there's almost certainly an ncurses function for it -- just check the man page, search online, or ask here on SO.
The ncurses dylib is available on all platforms, but for some reason the headers only exist on macOS. You shouldn't need it on iOS, watchOS, or tvOS anyway, because those platforms don't have terminals. There are two ways to exclude ncurses from being built on iOS, etc: 1) you can #ifdef out the headers, or 2) with Swift 5.3 you can declare a conditional target dependency, which is slightly simpler and cleaner.
Approach 1: #ifdef the headers
Use the following Sources/CCurses/curses.h file instead:
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#if !(defined TARGET_OS_IPHONE || defined TARGET_OS_WATCH || defined TARGET_OS_TV)
#include <curses.h>
#include <term.h>
#endif
Then, whenever you use a function from curses in your Swift code, surround it with a build conditional:
#if os(iOS) || os(watchOS) || os(tvOS)
useColor = false
#else
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
#endif
Approach 2: conditional target dependency (requires Swift 5.3)
No changes are necessary to the CCurses target; you only have to modify your dependency on CCurses in your Package.swift:
.target(
name: "MyLib",
dependencies: [
.target(name: "CCurses", condition: .when(platforms: [.macOS, .linux]))
]),
And use a build condition whenever you import CCurses or use curses functions:
#if canImport(CCurses)
import CCurses
#endif
// ...
#if canImport(CCurses)
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
#else
useColor = false
#endif

Conditional Compilation in assembler (.s) code for iPhone - how?

I have a few lines of assembler arm code in an .s file. Just a few routines i need to call. It works fine when building for the device, however when i switch to iPhone Simulator i get "no such instruction" errors. I tried to compile parts of the .s file conditionally with what i know:
#if !TARGET_IPHONE_SIMULATOR
But the assembler doesn't recognize these preprocessor directives (of course) and none of the conditional compilation techniques for assembler that i could remember or find worked, so i'm scratching my head now on how to avoid compilation of that assembler code when building for the Simulator. I also don't see a project option in Xcode that would allow me to compile the file or not depending on the target platform.
SOLVED:
All i was missing was the proper #import in the assembler file. I did not think of adding it because Xcode syntax highlighted any preprocessor directive in green (comment) which made me assume that these commands are not recognized when in fact they work just fine.
This works:
#import "TargetConditionals.h"
#if !TARGET_IPHONE_SIMULATOR
... asm code here ...
#endif
You do do it with a pre-processor macro. They are defined in TargetConditionals.h TARGET_IPHONE_SIMULATOR should be there! (You do need to #include it however.)
Here is code I use to detect ARM vs Thumb vs Simulator:
#include "TargetConditionals.h"
#if defined(__arm__)
# if defined(__thumb__)
# define COMPILE_ARM_THUMB_ASM 1
# else
# define COMPILE_ARM_ASM 1
# endif
#endif
#if TARGET_IPHONE_SIMULATOR
// Simulator defines
#else
// ARM or Thumb mode defines
#endif
// And here is how you might use it
uint32_t
test_compare_shifted_operand(uint32_t w1) {
uint32_t local;
#if defined(COMPILE_ARM_ASM)
const uint32_t shifted = (1 << 8);
__asm__ __volatile__ (
"mov %[w2], #1\n\t"
"cmp %[w2], %[w1], lsr #8\n\t"
"moveq %[w2], #10\n\t"
"movne %[w2], #11\n\t"
: \
[w1] "+l" (w1),
[w2] "+l" (local)
: \
[shifted] "l" (shifted)
);
#else // COMPILE_ARM_ASM
if ((w1 >> 8) == 1) {
local = 10;
} else {
local = 11;
}
#endif // COMPILE_ARM_ASM
return local;
}

ncurses and stdin blocking

I have stdin in a select() set and I want to take a string from stdin whenever the user types it and hits Enter.
But select is triggering stdin as ready to read before Enter is hit, and, in rare cases, before anything is typed at all. This hangs my program on getstr() until I hit Enter.
I tried setting nocbreak() and it's perfect really except that nothing gets echoed to the screen so I can't see what I'm typing. And setting echo() doesn't change that.
I also tried using timeout(0), but the results of that was even crazier and didn't work.
What you need to do is tho check if a character is available with the getch() function. If you use it in no-delay mode the method will not block. Then you need to eat up the characters until you encounter a '\n', appending each char to the resulting string as you go.
Alternatively - and the method I use - is to use the GNU readline library. It has support for non-blocking behavior, but documentation about that section is not so excellent.
Included here is a small example that you can use. It has a select loop, and uses the GNU readline library:
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>
#include <stdbool.h>
int quit = false;
void rl_cb(char* line)
{
if (NULL==line) {
quit = true;
return;
}
if(strlen(line) > 0) add_history(line);
printf("You typed:\n%s\n", line);
free(line);
}
int main()
{
struct timeval to;
const char *prompt = "# ";
rl_callback_handler_install(prompt, (rl_vcpfunc_t*) &rl_cb);
to.tv_sec = 0;
to.tv_usec = 10000;
while(1){
if (quit) break;
select(1, NULL, NULL, NULL, &to);
rl_callback_read_char();
};
rl_callback_handler_remove();
return 0;
}
Compile with:
gcc -Wall rl.c -lreadline