Estou tentando implementar o código de Ivan Kostoski para um medidor de nível de som baseado em ESP32 em um Seeed Xiao ESP32-C3.
De acordo com o wiki do Seeed , esse chip “é uma CPU RISC-V de 32 bits, que inclui uma FPU (Floating Point Unit) para aritmética de precisão simples de 32 bits”. Então, com base nisso, escrevi um código Assembly RISC-V para operações de ponto flutuante. no entanto, quando tento compilar, recebo o erro unrecognized opcode
em todas as instruções de ponto flutuante. Outras instruções não geram erros.
Aqui está a saída do console Arduino IDE. Observe como a primeira instrução ( addi
) não gera erro.
C:\...\cc4DgX57.s: Assembler messages:
C:\...\cc4DgX57.s:12: Error: unrecognized opcode `flw f0,0(a5)'
C:\...\cc4DgX57.s:13: Error: unrecognized opcode `flw f1,4(a5)'
C:\...\cc4DgX57.s:14: Error: unrecognized opcode `flw f2,8(a5)'
C:\...\cc4DgX57.s:15: Error: unrecognized opcode `flw f3,12(a5)'
C:\...\cc4DgX57.s:16: Error: unrecognized opcode `flw f4,0(a6)'
C:\...\cc4DgX57.s:17: Error: unrecognized opcode `flw f5,4(a6)'
C:\...\cc4DgX57.s:22: Error: unrecognized opcode `flw f6,a2'
C:\...\cc4DgX57.s:24: Error: unrecognized opcode `fmadd.s f6,f2,f4'
C:\...\cc4DgX57.s:25: Error: unrecognized opcode `fmadd.s f6,f3,f5'
C:\...\cc4DgX57.s:26: Error: unrecognized opcode `fmv.s f7,f6'
C:\...\cc4DgX57.s:27: Error: unrecognized opcode `fmadd.s f7,f0,f4'
C:\...\cc4DgX57.s:28: Error: unrecognized opcode `fmadd.s f7,f1,f5'
C:\...\cc4DgX57.s:29: Error: unrecognized opcode `fsw f7,a3'
C:\...\cc4DgX57.s:31: Error: unrecognized opcode `fmv.s f5,f4'
C:\...\cc4DgX57.s:32: Error: unrecognized opcode `fmv.s f4,f6'
C:\...\cc4DgX57.s:34: Error: backward ref to unknown label "1:"
C:\...\cc4DgX57.s:37: Error: unrecognized opcode `fsw f4,0(a6)'
C:\...\cc4DgX57.s:38: Error: unrecognized opcode `fsw f5,4(a6)'
C:\...\cc4DgX57.s:39: Error: unrecognized opcode `fmvi a2,0'
C:\...\cc4DgX57.s:48: Error: unrecognized opcode `flw f0,0(a5)'
C:\...\cc4DgX57.s:49: Error: unrecognized opcode `flw f1,4(a5)'
C:\...\cc4DgX57.s:50: Error: unrecognized opcode `flw f2,8(a5)'
C:\...\cc4DgX57.s:51: Error: unrecognized opcode `flw f3,12(a5)'
C:\...\cc4DgX57.s:52: Error: unrecognized opcode `flw f4,0(a6)'
C:\...\cc4DgX57.s:53: Error: unrecognized opcode `flw f5,4(a6)'
C:\...\cc4DgX57.s:54: Error: unrecognized opcode `fmv.s f6,a7'
C:\...\cc4DgX57.s:55: Error: unrecognized opcode `fli f10,0.0'
C:\...\cc4DgX57.s:56: Error: symbol `loop' is already defined
C:\...\cc4DgX57.s:59: Error: symbol `i' is already defined
C:\...\cc4DgX57.s:60: Error: unrecognized opcode `flw f7,a2'
C:\...\cc4DgX57.s:62: Error: unrecognized opcode `fmadd.s f7,f2,f4'
C:\...\cc4DgX57.s:63: Error: unrecognized opcode `fmadd.s f7,f3,f5'
C:\...\cc4DgX57.s:64: Error: unrecognized opcode `fmv.s f8,f7'
C:\...\cc4DgX57.s:65: Error: unrecognized opcode `fmadd.s f8,f0,f4'
C:\...\cc4DgX57.s:66: Error: unrecognized opcode `fmadd.s f8,f1,f5'
C:\...\cc4DgX57.s:67: Error: unrecognized opcode `fmul.s f9,f8,f6'
C:\...\cc4DgX57.s:68: Error: unrecognized opcode `fsw f9,a3'
C:\...\cc4DgX57.s:70: Error: unrecognized opcode `fmv.s f5,f4'
C:\...\cc4DgX57.s:71: Error: unrecognized opcode `fmv.s f4,f7'
C:\...\cc4DgX57.s:72: Error: unrecognized opcode `fmadd.s f10,f9,f9'
C:\...\cc4DgX57.s:74: Error: backward ref to unknown label "1:"
C:\...\cc4DgX57.s:76: Error: symbol `exit' is already defined
C:\...\cc4DgX57.s:77: Error: unrecognized opcode `fsw f4,a6,0'
C:\...\cc4DgX57.s:78: Error: unrecognized opcode `fsw f5,a6,4'
C:\...\cc4DgX57.s:79: Error: unrecognized opcode `fmr a2,f10'
C:\...\cc4DgX57.s: Error: local label `"1" (instance number 1 of a fb label)' is not defined
Este é o código de referência, escrito no Xtensa ISA, que estou tentando portar para o RISC-V:
extern "C" {
int sos_filter_f32(float *input, float *output, int len, const SOS_Coefficients &coeffs, SOS_Delay_State &w);
}
__asm__ (
//
// ESP32 implementation of IIR Second-Order Section filter
// Assumes a0 and b0 coefficients are one (1.0)
//
// float* a2 = input;
// float* a3 = output;
// int a4 = len;
// float* a5 = coeffs;
// float* a6 = w;
// float a7 = gain;
//
".text \n"
".align 4 \n"
".global sos_filter_f32 \n"
".type sos_filter_f32,@function\n"
"sos_filter_f32: \n"
" entry a1, 16 \n"
" lsi f0, a5, 0 \n" // float f0 = coeffs.b1;
" lsi f1, a5, 4 \n" // float f1 = coeffs.b2;
" lsi f2, a5, 8 \n" // float f2 = coeffs.a1;
" lsi f3, a5, 12 \n" // float f3 = coeffs.a2;
" lsi f4, a6, 0 \n" // float f4 = w[0];
" lsi f5, a6, 4 \n" // float f5 = w[1];
" loopnez a4, 1f \n" // for (; len>0; len--) {
" lsip f6, a2, 4 \n" // float f6 = *input++;
" madd.s f6, f2, f4 \n" // f6 += f2 * f4; // coeffs.a1 * w0
" madd.s f6, f3, f5 \n" // f6 += f3 * f5; // coeffs.a2 * w1
" mov.s f7, f6 \n" // f7 = f6; // b0 assumed 1.0
" madd.s f7, f0, f4 \n" // f7 += f0 * f4; // coeffs.b1 * w0
" madd.s f7, f1, f5 \n" // f7 += f1 * f5; // coeffs.b2 * w1 -> result
" ssip f7, a3, 4 \n" // *output++ = f7;
" mov.s f5, f4 \n" // f5 = f4; // w1 = w0
" mov.s f4, f6 \n" // f4 = f6; // w0 = f6
" 1: \n" // }
" ssi f4, a6, 0 \n" // w[0] = f4;
" ssi f5, a6, 4 \n" // w[1] = f5;
" movi.n a2, 0 \n" // return 0;
" retw.n \n"
);
extern "C" {
float sos_filter_sum_sqr_f32(float *input, float *output, int len, const SOS_Coefficients &coeffs, SOS_Delay_State &w, float gain);
}
__asm__ (
//
// ESP32 implementation of IIR Second-Order section filter with applied gain.
// Assumes a0 and b0 coefficients are one (1.0)
// Returns sum of squares of filtered samples
//
// float* a2 = input;
// float* a3 = output;
// int a4 = len;
// float* a5 = coeffs;
// float* a6 = w;
// float a7 = gain;
//
".text \n"
".align 4 \n"
".global sos_filter_sum_sqr_f32 \n"
".type sos_filter_sum_sqr_f32,@function \n"
"sos_filter_sum_sqr_f32: \n"
" entry a1, 16 \n"
" lsi f0, a5, 0 \n" // float f0 = coeffs.b1;
" lsi f1, a5, 4 \n" // float f1 = coeffs.b2;
" lsi f2, a5, 8 \n" // float f2 = coeffs.a1;
" lsi f3, a5, 12 \n" // float f3 = coeffs.a2;
" lsi f4, a6, 0 \n" // float f4 = w[0];
" lsi f5, a6, 4 \n" // float f5 = w[1];
" wfr f6, a7 \n" // float f6 = gain;
" const.s f10, 0 \n" // float sum_sqr = 0;
" loopnez a4, 1f \n" // for (; len>0; len--) {
" lsip f7, a2, 4 \n" // float f7 = *input++;
" madd.s f7, f2, f4 \n" // f7 += f2 * f4; // coeffs.a1 * w0
" madd.s f7, f3, f5 \n" // f7 += f3 * f5; // coeffs.a2 * w1;
" mov.s f8, f7 \n" // f8 = f7; // b0 assumed 1.0
" madd.s f8, f0, f4 \n" // f8 += f0 * f4; // coeffs.b1 * w0;
" madd.s f8, f1, f5 \n" // f8 += f1 * f5; // coeffs.b2 * w1;
" mul.s f9, f8, f6 \n" // f9 = f8 * f6; // f8 * gain -> result
" ssip f9, a3, 4 \n" // *output++ = f9;
" mov.s f5, f4 \n" // f5 = f4; // w1 = w0
" mov.s f4, f7 \n" // f4 = f7; // w0 = f7;
" madd.s f10, f9, f9 \n" // f10 += f9 * f9; // sum_sqr += f9 * f9;
" 1: \n" // }
" ssi f4, a6, 0 \n" // w[0] = f4;
" ssi f5, a6, 4 \n" // w[1] = f5;
" rfr a2, f10 \n" // return sum_sqr;
" retw.n \n" //
);
E aqui está meu código RISC-V:
extern "C" {
int sos_filter_f32(float *input, float *output, int len, const SOS_Coefficients &coeffs, SOS_Delay_State &w);
}
__asm__ (
//
// RISC-V implementation of IIR Second-Order Section filter
// Assumes a0 and b0 coefficients are one (1.0)
//
// float* a2 = input;
// float* a3 = output;
// int a4 = len;
// float* a5 = coeffs;
// float* a6 = w;
// float a7 = gain;
//
".text \n"
".p2align 2 \n"
".globl sos_filter_f32 \n"
".type sos_filter_f32,@function\n"
"sos_filter_f32: \n"
" addi sp, sp, -16 \n"
" flw f0, 0(a5) \n" // float f0 = coeffs.b1;
" flw f1, 4(a5) \n" // float f1 = coeffs.b2;
" flw f2, 8(a5) \n" // float f2 = coeffs.a1;
" flw f3, 12(a5) \n" // float f3 = coeffs.a2;
" flw f4, 0(a6) \n" // float f4 = w[0];
" flw f5, 4(a6) \n" // float f5 = w[1];
" loop: \n"
" bnez a4, 1f \n" // for (; len>0; len--) {
" j exit \n"
" i: \n"
" flw f6, a2 \n" // float f6 = *input++;
" addi a2, a2, 4 \n" // post-increment by 4
" fmadd.s f6, f2, f4 \n" // f6 += f2 * f4; // coeffs.a1 * w0
" fmadd.s f6, f3, f5 \n" // f6 += f3 * f5; // coeffs.a2 * w1
" fmv.s f7, f6 \n" // f7 = f6; // b0 assumed 1.0
" fmadd.s f7, f0, f4 \n" // f7 += f0 * f4; // coeffs.b1 * w0
" fmadd.s f7, f1, f5 \n" // f7 += f1 * f5; // coeffs.b2 * w1 -> result
" fsw f7, a3 \n" // *output++ = f7;
" addi a3, a3, 4 \n" // post-increment by 4
" fmv.s f5, f4 \n" // f5 = f4; // w1 = w0
" fmv.s f4, f6 \n" // f4 = f6; // w0 = f6
" addi a4, a4, -1 \n" // update loop counter
" bnez a4, 1b \n"
" j exit \n"
" exit: \n" // }
" fsw f4, 0(a6) \n" // w[0] = f4;
" fsw f5, 4(a6) \n" // w[1] = f5;
" fmvi a2, 0 \n" // return 0;
" ret \n"
);
extern "C" {
float sos_filter_sum_sqr_f32(float *input, float *output, int len, const SOS_Coefficients &coeffs, SOS_Delay_State &w, float gain);
}
__asm__ (
//
// RISC-V implementation of IIR Second-Order section filter with applied gain.
// Assumes a0 and b0 coefficients are one (1.0)
// Returns sum of squares of filtered samples
//
// float* a2 = input;
// float* a3 = output;
// int a4 = len;
// float* a5 = coeffs;
// float* a6 = w;
// float a7 = gain;
//
".text \n"
".p2align 2 \n"
".globl sos_filter_sum_sqr_f32 \n"
".type sos_filter_sum_sqr_f32,@function \n"
"sos_filter_sum_sqr_f32: \n"
" addi sp, sp, -16 \n"
" flw f0, 0(a5) \n" // float f0 = coeffs.b1;
" flw f1, 4(a5) \n" // float f1 = coeffs.b2;
" flw f2, 8(a5) \n" // float f2 = coeffs.a1;
" flw f3, 12(a5) \n" // float f3 = coeffs.a2;
" flw f4, 0(a6) \n" // float f4 = w[0];
" flw f5, 4(a6) \n" // float f5 = w[1];
" fmv.s f6, a7 \n" // float f6 = gain;
" fli f10, 0.0 \n" // float sum_sqr = 0;
" loop: \n"
" bnez a4, 1f \n" // for (; len>0; len--) {
" j exit \n"
" i: \n"
" flw f7, a2 \n" // float f7 = *input++;
" addi a2, a2, 4 \n" // post-increment by 4
" fmadd.s f7, f2, f4 \n" // f7 += f2 * f4; // coeffs.a1 * w0
" fmadd.s f7, f3, f5 \n" // f7 += f3 * f5; // coeffs.a2 * w1;
" fmv.s f8, f7 \n" // f8 = f7; // b0 assumed 1.0
" fmadd.s f8, f0, f4 \n" // f8 += f0 * f4; // coeffs.b1 * w0;
" fmadd.s f8, f1, f5 \n" // f8 += f1 * f5; // coeffs.b2 * w1;
" fmul.s f9, f8, f6 \n" // f9 = f8 * f6; // f8 * gain -> result
" fsw f9, a3 \n" // *output++ = f9;
" addi a3, a3, 4 \n" // post-increment by 4
" fmv.s f5, f4 \n" // f5 = f4; // w1 = w0
" fmv.s f4, f7 \n" // f4 = f7; // w0 = f7;
" fmadd.s f10, f9, f9 \n" // f10 += f9 * f9; // sum_sqr += f9 * f9;
" addi a4, a4, -1 \n" // update loop counter
" bnez a4, 1b \n"
" j exit \n"
" exit: \n" // }
" fsw f4, a6, 0 \n" // w[0] = f4;
" fsw f5, a6, 4 \n" // w[1] = f5;
" fmr a2, f10 \n" // return sum_sqr;
" ret \n" //
);
Estou usando instruções de precisão única que, segundo o fabricante, devem ser suportadas. Ainda assim, o unrecognized opcode
erro sugere o contrário.
Tenho a placa instalada corretamente no IDE do Arduino e a selecionei antes de compilar, então não é que o código esteja sendo compilado para um chip RISC-V não relacionado sem suporte de ponto flutuante.
O resto do meu código não é o problema, já que portei as funções acima para C++ (seguindo os comentários de Ivan sobre o código Assembly) e elas compilam e funcionam.
Seeed Studio não é o fabricante. Eles vendem placas de desenvolvimento que usam o ESP32-C3 (entre outras CPUs), mas não fabricam o ESP32-C3. O Espressif sim, e o principal é consultar o documento de referência técnica do Espressif para a CPU, não as informações de vendas do Seeed Studio.
As folhas de dados raramente indicam o que não é suportado, portanto a referência técnica nunca indica claramente "extensões de ponto flutuante não suportadas". Existem muitas versões de CPUs RISC V; Espressif descreve a CPU usada no C3 como suportando "extensões padrão de número inteiro base (I), multiplicação/divisão (M) e compactação (C)" (1.1 Visão geral da CPU ESP RISC V).
Posteriormente no documento, é mostrado que indica que extensões de ponto flutuante de precisão simples, dupla e quádrupla estão indisponíveis (sinalizadores no
misa
registro - Machine ISA -).As instruções que você está tentando usar -
fmadd.s
,fmv.s
, etc - fazem parte das instruções computacionais flutuantes que omisa
registro indica que não são suportadas, e é por isso que o código que você escreveu não é compilado.Comparando o ESP32-C3 com outras CPUs ESP32, você pode considerá-lo o “processador de valor” da linha. As atuais CPUs RISC-V da Espressif têm menos memória interna, são de núcleo único, não suportam PSRAM e não possuem extensões computacionais de ponto flutuante RISC-V simples, duplas ou quádruplas. As outras CPUs da Espressif (as ESP32, ESP32-S2 e ESP32-S3 originais) são todas mais capazes de várias maneiras e custam mais.
Minha melhor sugestão de alternativa para você é fazer o que @Peter Cordes sugeriu em um comentário; inspecione a saída do código assembly do compilador e baseie seu código no que você encontrar lá.