No Modulo / Remainder Operator "%" for integer types
The integer modulo (remainder) Operator "%" insn't implemented...
Demo code:
#include "intN_t.h"
#pragma MAIN_MHZ test 100.0
int32_t test(int32_t a, int32_t b){
return a % b; // not implemented
}
Tool Output:
GET_BIN_OP_MOD_C_CODE Only mod between float for now!
Possible [Slow | Non-Generic] Fix is overloading the operator:
int32_t BIN_OP_MOD_int32_t_int32_t(int32_t left, int32_t right){
return left - ((left / right) * right);
}
This generates:
BIN_OP_DIV_int32_t_int32_t BIN_OP_MINUS_uint32_t_uint1_t UNARY_OP_NOT_uint32_t MUX_uint1_t_uint32_t_uint32_t BIN_OP_DIV_uint32_t_uint32_t BIN_OP_GTE_uint32_t_uint32_t BIN_OP_MINUS_int33_t_int33_t UNARY_OP_NOT_uint1_t BIN_OP_MINUS_uint32_t_uint32_t BIN_OP_XOR_uint1_t_uint1_t MUX_uint1_t_int32_t_int32_t UNARY_OP_NEGATE_uint32_t UNARY_OP_NOT_uint33_t BIN_OP_PLUS_uint33_t_uint1_t BIN_OP_INFERRED_MULT_int32_t_int32_t BIN_OP_MINUS_int32_t_int64_t
My log of notes in implementing this
Run code trying to use %
Get error
GET_BIN_OP_MOD_C_CODE Only mod between float for now!
Find error message in .py
inside function
def GET_BIN_OP_MOD_C_CODE(partially_complete_logic, out_dir):
This function is saying
given some function defintion of comb logic that is partially filled out
(know func signature, not the body)
return the PipelineC code that implements the % operation in terms of simpler ops
partially_complete_ meaning
ex. we know the input types
looking them up by input argument name string
left_t = partially_complete_logic.wire_to_c_type[partially_complete_logic.inputs[0]]
right_t = partially_complete_logic.wire_to_c_type[partially_complete_logic.inputs[1]]
Which leads us to error out if the types arent floats atm.
Inside def GET_BIN_OP_MOD_C_CODE(partially_complete_logic, out_dir):
happens to be some commented out code
that calls GET_BIN_OP_DIV_UINT_N_C_CODE(partially_complete_logic, out_dir)
that is past me saying - do something like was done for DIV
Notice DIV_UINT_N for that func, its typed, since uint div without sign bit handled was done first, signed after, inefficient duplicated code, etc meh
Lets do signed integers first actually
There is also commented out code that was checking types too
if VHDL.WIRES_ARE_UINT_N(partially_complete_logic.inputs, partially_complete_logic)
asking for a list of wire names in some comb logic , are all those wires uint?
well use the signed version ARE_INT_N
if VHDL.WIRES_ARE_INT_N(partially_complete_logic.inputs, partially_complete_logic):
return GET_BIN_OP_MOD_INT_N_C_CODE(partially_complete_logic, out_dir)
and change the error message to say only for floats and ints for now...
So we introduced GET_BIN_OP_MOD_INT_N_C_CODE where should that live?
Again, doing something like DIV, lets find GET_BIN_OP_DIV_INT_N_C_CODE
and copy that as a start
def GET_BIN_OP_DIV_INT_N_C_CODE(partially_complete_logic, out_dir, parser_state):
to
def GET_BIN_OP_MOD_INT_N_C_CODE(partially_complete_logic, out_dir, parser_state):
At this point some point in past forget to get the parser_state arg propogated down into this func call, minor change to note that was fixed.
After copying GET_BIN_OP_DIV_INT_N_C_CODE we have a function that generates PipelineC to do divison.
It generates code to remove the sign bit, convert to absolute value unsigned, do unsigned div, and then fix sign bit at the end
Hopefully MOD fits into doing that and doesnt need custom implementation for signed MOD not based on unsigned MOD...
So for now, in that copied code just changing the division /, etc stuff to a % and will revisit.
Running pipelinec on the original example code again
youll see it elaborate % for signed into absolute value stuff
and then fail printing our modified error message
GET_BIN_OP_MOD_C_CODE Only mod between float and ints for now!
Back into GET_BIN_OP_MOD_C_CODE
we will add the unsigned version
elif VHDL.WIRES_ARE_UINT_N(partially_complete_logic.inputs, partially_complete_logic):
return GET_BIN_OP_MOD_UINT_N_C_CODE(partially_complete_logic, out_dir, parser_state)
and change error message to something like not floats or ints wtf?
make it an exception too why not....
raise Exception(f"Modulo between unknown types {left_t} % {right_t}?")
dont have source available in the section of compiler to point to where specifically in code the modulo is
if wanted to do that, it is done in upper layers that for ex. generated partially_complete_logic to start with
Similar to before
copy the DIV version to start
def GET_BIN_OP_DIV_UINT_N_C_CODE(partially_complete_logic, out_dir, parser_state):
rename it GET_BIN_OP_MOD_UINT_N_C_CODE
This function generates PipelineC code to do unsigned division like https://en.wikipedia.org/wiki/Division_algorithm#Restoring_division
it was written before PipelineC supported for loops
and so the loops are done in python to unroll and print verbose generated lines of C code
and would you look at that, there was already a remainder variable in that DIV code
its just unused / not output at the end
(maybe should make some built in div_mod func that returns both values from one op?)
but now our MOD func will output the remainder instead of the DIV output
return remainder; is pretty much the only difference from DIV generated code
this is absolutely another reminder about the TODOs for sharing more of the DIV and MOD code gen, needlessly copied but outside scope of just implementing mod...
Running the example code again now youll see the component pieces being found
Elaborating main function hierarchies down to raw HDL logic...
... found: int32_abs
... found: BIN_OP_MOD_int32_t_int32_t
... found: BIN_OP_MOD_uint32_t_uint32_t
In the output directory you can see the generated C code for those ops as described in the python:
pipelinec_output/built_in/BIN_OP_MOD_int32_t_int32_t/BIN_OP_MOD_int32_t_int32_t.c
#include "intN_t.h"
#include "uintN_t.h"
#include "bit_manip.h"
// 32b % 32b mod
int32_t BIN_OP_MOD_int32_t_int32_t(int32_t left, int32_t right)
{
// Record sign bits
uint1_t l_signed = int32_31_31(left);
uint1_t r_signed = int32_31_31(right);
// Resize to unsigned values of same width
uint32_t left_resized = int32_abs(left);
uint32_t right_resized = int32_abs(right);
// Do mod on uints
uint32_t unsigned_result = left_resized % right_resized;
// Adjust sign
int32_t output = unsigned_result;
if(l_signed ^ r_signed)
{
output = -unsigned_result;
}
return output;
}
And should be done assuming the sign bit fixing using unsigned mod to do signed mod makes sense...
https://github.com/JulianKemmerer/PipelineC/commit/09fb8a3e267c9839b007a39a264602290cb55a05