From 50f56077993b5b00392fbaf477f3b6c85d46c1fb Mon Sep 17 00:00:00 2001 From: David Harris Date: Sun, 27 Feb 2022 20:35:01 +0000 Subject: [PATCH] New softfloat_calc program --- .gitignore | 1 + examples/fp/softfloat_calc/softfloat_calc.c | 211 ++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 examples/fp/softfloat_calc/softfloat_calc.c diff --git a/.gitignore b/.gitignore index 6ea4b650..71d4771a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ examples/asm/example/example examples/C/sum/sum examples/C/fir/fir examples/fp/softfloat_demo/softfloat_demo +examples/fp/softfloat_calc/softfloat_calc pipelined/src/fma/fma16_testgen linux/devicetree/debug/* !linux/devicetree/debug/dump-dts.sh diff --git a/examples/fp/softfloat_calc/softfloat_calc.c b/examples/fp/softfloat_calc/softfloat_calc.c new file mode 100644 index 00000000..a3396cf2 --- /dev/null +++ b/examples/fp/softfloat_calc/softfloat_calc.c @@ -0,0 +1,211 @@ +// softfloat_calc.c +// David_Harris@hmc.edu 27 February 2022 +// +// Use SoftFloat as a calculator + +#include +#include +#include +#include +#include "softfloat.h" +#include "softfloat_types.h" + +typedef union hp { + uint16_t v; + float16_t h; +} hp; + +typedef union sp { + uint32_t v; + float32_t ft; + float f; +} sp; + +typedef union dp { + uint64_t v; + double d; +} dp; + + +int opSize = 0; + +void printF16(char *msg, float16_t f) { + hp convh; + sp convf; + float32_t temp; + convh.v = f.v; // use union to convert between hexadecimal and floating-point views + temp = f16_to_f32(convh.h); + convf.ft = temp; + printf ("%s: 0x%04x = %g\n", msg, convh.v, convf.f); // no easy way to print half prec. +} + +void printF32(char *msg, float32_t f) { + sp conv; + conv.v = f.v; // use union to convert between hexadecimal and floating-point views + printf ("%s: 0x%08x = %g\n", msg, conv.v, conv.f); +} + +void printF64(char *msg, float64_t f) { + dp conv; + conv.v = f.v; // use union to convert between hexadecimal and floating-point views + printf ("%s: 0x%016lx = %lg\n", msg, conv.v, conv.d); +} + +void printFlags(void) { + int NX = softfloat_exceptionFlags % 2; + int UF = (softfloat_exceptionFlags >> 1) % 2; + int OF = (softfloat_exceptionFlags >> 2) % 2; + int DZ = (softfloat_exceptionFlags >> 3) % 2; + int NV = (softfloat_exceptionFlags >> 4) % 2; + printf ("exceptions: Inexact %d Underflow %d Overflow %d DivideZero %d Invalid %d\n", + NX, UF, OF, DZ, NV); +} + +void softfloatInit(void) { + // rounding modes: RNE: softfloat_round_near_even + // RZ: softfloat_round_minMag + // RP: softfloat_round_max + // RM: softfloat_round_min + softfloat_roundingMode = softfloat_round_near_even; + softfloat_exceptionFlags = 0; // clear exceptions + softfloat_detectTininess = softfloat_tininess_beforeRounding; // RISC-V behavior for tininess +} + +uint64_t parseNum(char *num) { + uint64_t result; + int size; // size of operands in bytes (2= half, 4=single, 8 = double) + if (strlen(num) < 8) size = 2; + else if (strlen(num) < 16) size = 4; + else if (strlen(num) < 19) size = 8; + else { + printf("Error: only half, single, and double precision supported"); + exit(1); + } + if (opSize != 0) { + if (size != opSize) { + printf("Error: inconsistent operand sizes %d and %d\n", size, opSize); + exit(1); + } + } else { + opSize = size; + //printf ("Operand size is %d\n", opSize); + } + result = (uint64_t)strtoul(num, NULL, 16); + //printf("Parsed %s as 0x%lx\n", num, result); + return result; +} + +char parseOp(char *op) { + if (strlen(op) > 1) { + printf ("Bad op %s must be 1 character\n", op); + exit(1); + } else { + return op[0]; + } +} + +char parseRound(char *rnd) { + if (strcmp(rnd, "RNE") == 0) return softfloat_round_near_even; + else if (strcmp(rnd, "RZ") == 0) return softfloat_round_minMag; + else if (strcmp(rnd, "RP") == 0) return softfloat_round_max; + else if (strcmp(rnd, "RM") == 0) return softfloat_round_min; + else { + printf("Rounding mode of %s is not known\n", rnd); + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + uint64_t xn, yn, zn; + char op1, op2; + char cmd[80]; + + softfloatInit(); + + if (argc < 4 || argc > 7) { + printf("Usage: %s x op y [RNE/RZ/RM/RP] or x x y + z [RNE/RZ/RM/RP]\n Example: 0x3f800000 + 0x3fC00000\n Use x for multiplication\n", argv[0]); + exit(1); + } else { + softfloat_roundingMode = softfloat_round_near_even; + xn = parseNum(argv[1]); + yn = parseNum(argv[3]); + op1 = parseOp(argv[2]); + if (argc == 5) softfloat_roundingMode = parseRound(argv[4]); + if (argc >= 6) { + zn = parseNum(argv[5]); + op2 = parseOp(argv[4]); + if (argc == 7) softfloat_roundingMode = parseRound(argv[6]); + if (op1 != 'x' || op2 != '+') { + printf("Error: only x * y + z supported for 3-input operations, not %c %c\n", op1, op2); + } + else { + if (opSize == 2) { + float16_t x, y, z, r; + x.v = xn; y.v = yn; z.v = zn; + r = f16_mulAdd(x, y, z); + printF16("X", x); printF16("Y", y); printF16("Z", z); + printF16("result = X*Y+Z", r); printFlags(); + } else if (opSize == 4) { + float32_t x, y, z, r; + x.v = xn; y.v = yn; z.v = zn; + r = f32_mulAdd(x, y, z); + printF32("X", x); printF32("Y", y); printF32("Z", z); + printF32("result = X*Y+Z", r); printFlags(); + } else { // opSize = 8 + float64_t x, y, z, r; + x.v = xn; y.v = yn; z.v = zn; + r = f64_mulAdd(x, y, z); + printF64("X", x); printF64("Y", y); printF64("Z", z); + printF64("result = X*Y+Z", r); printFlags(); + } + } + } else { + if (opSize == 2) { + float16_t x, y, r; + x.v = xn; y.v = yn; + switch (op1) { + case 'x': r = f16_mul(x, y); break; + case '+': r = f16_add(x, y); break; + case '-': r = f16_sub(x, y); break; + case '/': r = f16_div(x, y); break; + case '%': r = f16_rem(x, y); break; + default: printf("Unknown op %c\n", op1); exit(1); + } + printF16("X", x); printF16("Y", y); + sprintf(cmd, "0x%04x %c 0x%04x", x.v, op1, y.v); + printF16(cmd, r); printFlags(); + } else if (opSize == 4) { + float32_t x, y, r; + x.v = xn; y.v = yn; + switch (op1) { + case 'x': r = f32_mul(x, y); break; + case '+': r = f32_add(x, y); break; + case '-': r = f32_sub(x, y); break; + case '/': r = f32_div(x, y); break; + case '%': r = f32_rem(x, y); break; + default: printf("Unknown op %c\n", op1); exit(1); + } + printF32("X", x); printF32("Y", y); + sprintf(cmd, "0x%08x %c 0x%08x", x.v, op1, y.v); + printF32(cmd, r); printFlags(); + + } else { // opSize = 8 + float64_t x, y, r; + x.v = xn; y.v = yn; + switch (op1) { + case 'x': r = f64_mul(x, y); break; + case '+': r = f64_add(x, y); break; + case '-': r = f64_sub(x, y); break; + case '/': r = f64_div(x, y); break; + case '%': r = f64_rem(x, y); break; + default: printf("Unknown op %c\n", op1); exit(1); + } + printF64("X", x); printF64("Y", y); + sprintf(cmd, "0x%016lx %c 0x%016lx", x.v, op1, y.v); + printF64(cmd, r); printFlags(); + + } + } + } +}