diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 8a993098a..679dbfad5 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - - uses: numworks/setup-arm-toolchain@v1 + - uses: numworks/setup-arm-toolchain@2020-q2 - uses: actions/checkout@v1 with: submodules: true @@ -76,7 +76,7 @@ jobs: runs-on: ubuntu-latest steps: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - - uses: numworks/setup-arm-toolchain@v1 + - uses: numworks/setup-arm-toolchain@2020-q2 - uses: actions/checkout@v1 with: submodules: true @@ -97,7 +97,7 @@ jobs: web: runs-on: ubuntu-latest steps: - - uses: numworks/setup-emscripten@v2 + - uses: numworks/setup-emscripten@v1 with: sdk: latest-upstream - uses: actions/checkout@v1 diff --git a/.github/workflows/metric-workflow.yml b/.github/workflows/metric-workflow.yml index 0a837d2d4..4a024853d 100644 --- a/.github/workflows/metric-workflow.yml +++ b/.github/workflows/metric-workflow.yml @@ -1,5 +1,5 @@ name: Metrics -on: [pull_request] +on: [pull_request_target] jobs: binary-size: @@ -8,7 +8,7 @@ jobs: - name: Install dependencies run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - name: Install ARM toolchain - uses: numworks/setup-arm-toolchain@v1 + uses: numworks/setup-arm-toolchain@2020-q2 - name: Checkout PR base uses: actions/checkout@v2 with: @@ -27,12 +27,15 @@ jobs: run: make -j2 -C head epsilon.elf - name: Retrieve binary size analysis id: binary_size - run: echo "::set-output name=table::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --sections .text .rodata .bss .data --custom 'Total (RAM)' .data .bss --custom 'Total (ROM)' .text .rodata .data --escape)" - - name: Prepare comment auth - run: echo "::set-env name=GITHUB_TOKEN::$(echo YjgxYTk1YTQ4YzYxNjU4ZTA3YWQzNDYwNTk3ZTI2MTlkODU5MThlOQo= | base64 --decode)" + run: echo "::set-output name=table::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --sections .text .rodata .bss .data --escape)" - name: Add comment - uses: actions/github@v1.0.0 - env: - GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} + uses: actions/github-script@v3.0.0 with: - args: comment ${{ steps.binary_size.outputs.table }} + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + await github.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: `${{ steps.binary_size.outputs.table }}`, + }); diff --git a/apps/graph/graph/graph_view.cpp b/apps/graph/graph/graph_view.cpp index 34635f8d1..4d5b3c5b7 100644 --- a/apps/graph/graph/graph_view.cpp +++ b/apps/graph/graph/graph_view.cpp @@ -57,7 +57,12 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { ContinuousFunction * f = (ContinuousFunction *)model; Poincare::Context * c = (Poincare::Context *)context; return f->evaluateXYAtParameter(t, c); - }, f.operator->(), context(), f->color(), true, record == m_selectedRecord, m_highlightedStart, m_highlightedEnd); + }, f.operator->(), context(), f->color(), true, record == m_selectedRecord, m_highlightedStart, m_highlightedEnd, + [](double t, void * model, void * context) { + ContinuousFunction * f = (ContinuousFunction *)model; + Poincare::Context * c = (Poincare::Context *)context; + return f->evaluateXYAtParameter(t, c); + }); /* Draw tangent */ if (m_tangent && record == m_selectedRecord) { float tangentParameterA = f->approximateDerivative(m_curveViewCursor->x(), context()); diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index 4d0e17b12..4eeb6e2af 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -413,6 +413,10 @@ void CurveView::drawHorizontalOrVerticalSegment(KDContext * ctx, KDRect rect, Ax if (dashSize < 0) { // Continuous segment is equivalent to one big dash dashSize = end - start; + if (dashSize < 0) { + // end-start overflowed + dashSize = KDCOORDINATE_MAX; + } } KDRect lineRect = KDRectZero; for (KDCoordinate i = start; i < end; i += 2*dashSize) { @@ -427,6 +431,10 @@ void CurveView::drawHorizontalOrVerticalSegment(KDContext * ctx, KDRect rect, Ax if (rect.intersects(lineRect)) { ctx->fillRect(lineRect, color); } + if (i > KDCOORDINATE_MAX - 2*dashSize) { + // Avoid overflowing KDCoordinate + break; + } } } @@ -595,7 +603,7 @@ const uint8_t thickStampMask[(thickStampSize+1)*(thickStampSize+1)] = { constexpr static int k_maxNumberOfIterations = 10; -void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForParameter xyEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound) const { +void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, EvaluateXYForDoubleParameter xyDoubleEvaluation) const { float previousT = NAN; float t = NAN; float previousX = NAN; @@ -617,17 +625,17 @@ void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd } previousX = x; previousY = y; - Coordinate2D xy = xyEvaluation(t, model, context); + Coordinate2D xy = xyFloatEvaluation(t, model, context); x = xy.x1(); y = xy.x2(); if (colorUnderCurve && !std::isnan(x) && colorLowerBound < x && x < colorUpperBound && !(std::isnan(y) || std::isinf(y))) { drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, x, std::min(0.0f, y), std::max(0.0f, y), color, 1); } - joinDots(ctx, rect, xyEvaluation, model, context, drawStraightLinesEarly, previousT, previousX, previousY, t, x, y, color, thick, k_maxNumberOfIterations); + joinDots(ctx, rect, xyFloatEvaluation, model, context, drawStraightLinesEarly, previousT, previousX, previousY, t, x, y, color, thick, k_maxNumberOfIterations, xyDoubleEvaluation); } while (true); } -void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForParameter xyEvaluation, void * model, void * context, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound) const { +void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, EvaluateXYForDoubleParameter xyDoubleEvaluation) const { float rectLeft = pixelToFloat(Axis::Horizontal, rect.left() - k_externRectMargin); float rectRight = pixelToFloat(Axis::Horizontal, rect.right() + k_externRectMargin); float tStart = std::isnan(rectLeft) ? xMin : std::max(xMin, rectLeft); @@ -637,7 +645,7 @@ void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, flo return; } float tStep = pixelWidth(); - drawCurve(ctx, rect, tStart, tEnd, tStep, xyEvaluation, model, context, true, color, thick, colorUnderCurve, colorLowerBound, colorUpperBound); + drawCurve(ctx, rect, tStart, tEnd, tStep, xyFloatEvaluation, model, context, true, color, thick, colorUnderCurve, colorLowerBound, colorUpperBound, xyDoubleEvaluation); } void CurveView::drawHistogram(KDContext * ctx, KDRect rect, EvaluateYForX yEvaluation, void * model, void * context, float firstBarAbscissa, float barWidth, @@ -678,7 +686,12 @@ void CurveView::drawHistogram(KDContext * ctx, KDRect rect, EvaluateYForX yEvalu } } -void CurveView::joinDots(KDContext * ctx, KDRect rect, EvaluateXYForParameter xyEvaluation , void * model, void * context, bool drawStraightLinesEarly, float t, float x, float y, float s, float u, float v, KDColor color, bool thick, int maxNumberOfRecursion) const { +static bool pointInBoundingBox(float x1, float y1, float x2, float y2, float xC, float yC) { + return ((x1 <= xC && xC <= x2) || (x2 <= xC && xC <= x1)) + && ((y1 <= yC && yC <= y2) || (y2 <= yC && yC <= y1)); +} + +void CurveView::joinDots(KDContext * ctx, KDRect rect, EvaluateXYForFloatParameter xyFloatEvaluation , void * model, void * context, bool drawStraightLinesEarly, float t, float x, float y, float s, float u, float v, KDColor color, bool thick, int maxNumberOfRecursion, EvaluateXYForDoubleParameter xyDoubleEvaluation) const { const bool isFirstDot = std::isnan(t); const bool isLeftDotValid = !( std::isnan(x) || std::isinf(x) || @@ -701,25 +714,42 @@ void CurveView::joinDots(KDContext * ctx, KDRect rect, EvaluateXYForParameter xy || (!isLeftDotValid && maxNumberOfRecursion == 0) // Last step of the recursion with an undefined left dot: we stamp the last right dot || (isLeftDotValid && deltaX*deltaX + deltaY*deltaY < circleDiameter * circleDiameter / 4.0f)) { // the dots are already close enough // the dots are already joined - stampAtLocation(ctx, rect, puf, pvf, color, thick); + /* We need to be sure that the point is not an artifact caused by error + * in float approximation. */ + float pvd = xyDoubleEvaluation ? floatToPixel(Axis::Vertical, static_cast(xyDoubleEvaluation(u, model, context).x2())) : pvf; + stampAtLocation(ctx, rect, puf, pvd, color, thick); return; } } // Middle point float ct = (t + s)/2.0f; - Coordinate2D cxy = xyEvaluation(ct, model, context); + Coordinate2D cxy = xyFloatEvaluation(ct, model, context); float cx = cxy.x1(); float cy = cxy.x2(); - if ((drawStraightLinesEarly || maxNumberOfRecursion == 0) && isRightDotValid && isLeftDotValid && - ((x <= cx && cx <= u) || (u <= cx && cx <= x)) && ((y <= cy && cy <= v) || (v <= cy && cy <= y))) { + if ((drawStraightLinesEarly || maxNumberOfRecursion <= 0) && isRightDotValid && isLeftDotValid && + pointInBoundingBox(x, y, u, v, cx, cy)) { /* As the middle dot is between the two dots, we assume that we * can draw a 'straight' line between the two */ - straightJoinDots(ctx, rect, pxf, pyf, puf, pvf, color, thick); - return; + + constexpr float dangerousSlope = 1e6f; + if (xyDoubleEvaluation && std::fabs((v-y) / (u-x)) > dangerousSlope) { + /* We need to make sure we're not drawing a vertical asymptote because of + * rounding errors. */ + Coordinate2D xyD = xyDoubleEvaluation(static_cast(t), model, context); + Coordinate2D uvD = xyDoubleEvaluation(static_cast(s), model, context); + Coordinate2D cxyD = xyDoubleEvaluation(static_cast(ct), model, context); + if (pointInBoundingBox(xyD.x1(), xyD.x2(), uvD.x1(), uvD.x2(), cxyD.x1(), cxyD.x2())) { + straightJoinDots(ctx, rect, floatToPixel(Axis::Horizontal, xyD.x1()), floatToPixel(Axis::Vertical, xyD.x2()), floatToPixel(Axis::Horizontal, uvD.x1()), floatToPixel(Axis::Vertical, uvD.x2()), color, thick); + return; + } + } else { + straightJoinDots(ctx, rect, pxf, pyf, puf, pvf, color, thick); + return; + } } if (maxNumberOfRecursion > 0) { - joinDots(ctx, rect, xyEvaluation, model, context, drawStraightLinesEarly, t, x, y, ct, cx, cy, color, thick, maxNumberOfRecursion-1); - joinDots(ctx, rect, xyEvaluation, model, context, drawStraightLinesEarly, ct, cx, cy, s, u, v, color, thick, maxNumberOfRecursion-1); + joinDots(ctx, rect, xyFloatEvaluation, model, context, drawStraightLinesEarly, t, x, y, ct, cx, cy, color, thick, maxNumberOfRecursion-1, xyDoubleEvaluation); + joinDots(ctx, rect, xyFloatEvaluation, model, context, drawStraightLinesEarly, ct, cx, cy, s, u, v, color, thick, maxNumberOfRecursion-1, xyDoubleEvaluation); } } diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index 18985cb54..983d802bb 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -16,7 +16,8 @@ public: /* We want a 3 characters margin before the first label tick, so that most * labels appear completely. This gives 3*charWidth/320 = 3*7/320= 0.066 */ static constexpr float k_labelsHorizontalMarginRatio = 0.066f; - typedef Poincare::Coordinate2D (*EvaluateXYForParameter)(float t, void * model, void * context); + typedef Poincare::Coordinate2D (*EvaluateXYForFloatParameter)(float t, void * model, void * context); + typedef Poincare::Coordinate2D (*EvaluateXYForDoubleParameter)(double t, void * model, void * context); typedef float (*EvaluateYForX)(float x, void * model, void * context); enum class Axis { Horizontal = 0, @@ -106,8 +107,8 @@ protected: void drawGrid(KDContext * ctx, KDRect rect) const; void drawAxes(KDContext * ctx, KDRect rect) const; void drawAxis(KDContext * ctx, KDRect rect, Axis axis) const; - void drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForParameter xyEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f) const; - void drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForParameter xyEvaluation, void * model, void * context, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f) const; + void drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, EvaluateXYForDoubleParameter xyDoubleEvaluation = nullptr) const; + void drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, EvaluateXYForDoubleParameter xyDoubleEvaluation = nullptr) const; void drawHistogram(KDContext * ctx, KDRect rect, EvaluateYForX yEvaluation, void * model, void * context, float firstBarAbscissa, float barWidth, bool fillBar, KDColor defaultColor, KDColor highlightColor, float highlightLowerBound = INFINITY, float highlightUpperBound = -INFINITY) const; void computeLabels(Axis axis); @@ -137,7 +138,7 @@ private: int numberOfLabels(Axis axis) const; /* Recursively join two dots (dichotomy). The method stops when the * maxNumberOfRecursion in reached. */ - void joinDots(KDContext * ctx, KDRect rect, EvaluateXYForParameter xyEvaluation, void * model, void * context, bool drawStraightLinesEarly, float t, float x, float y, float s, float u, float v, KDColor color, bool thick, int maxNumberOfRecursion) const; + void joinDots(KDContext * ctx, KDRect rect, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, bool drawStraightLinesEarly, float t, float x, float y, float s, float u, float v, KDColor color, bool thick, int maxNumberOfRecursion, EvaluateXYForDoubleParameter xyDoubleEvaluation = nullptr) const; /* Join two dots with a straight line. */ void straightJoinDots(KDContext * ctx, KDRect rect, float pxf, float pyf, float puf, float pvf, KDColor color, bool thick) const; /* Stamp centered around (pxf, pyf). If pxf and pyf are not round number, the diff --git a/build/targets.device.mak b/build/targets.device.mak index 3de0134aa..9498371c4 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -1,6 +1,6 @@ include build/targets.device.$(MODEL).mak -HANDY_TARGETS += flasher.light flasher.verbose bench.ram bench.flash +HANDY_TARGETS += flasher.light flasher.verbose flasher.verbose.flash bench.ram bench.flash HANDY_TARGETS_EXTENSIONS += dfu hex bin %_ram_map: %.$(EXE) @@ -42,8 +42,10 @@ openocd: flasher_src = $(ion_src) $(ion_device_flasher_src) $(liba_src) $(kandinsky_src) $(BUILD_DIR)/flasher.light.$(EXE): $(call flavored_object_for,$(flasher_src),light usbxip) $(BUILD_DIR)/flasher.verbose.$(EXE): $(call flavored_object_for,$(flasher_src),usbxip) +$(BUILD_DIR)/flasher.verbose.flash.$(EXE): $(call flavored_object_for,$(flasher_src)) $(BUILD_DIR)/flasher.%.$(EXE): LDFLAGS += -Lion/src/$(PLATFORM)/flasher $(BUILD_DIR)/flasher.%.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld +$(BUILD_DIR)/flasher.%.flash.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/$(MODEL)/internal_flash.ld #TODO Do not build all apps... Put elsewhere? bench_src = $(ion_src) $(liba_src) $(kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_device_bench_src) diff --git a/ion/include/ion/usb.h b/ion/include/ion/usb.h index a629aa5cb..e5760bb94 100644 --- a/ion/include/ion/usb.h +++ b/ion/include/ion/usb.h @@ -8,7 +8,7 @@ bool isPlugged(); bool isEnumerated(); // Speed-enumerated, to be accurate void clearEnumerationInterrupt(); -void DFU(); +void DFU(bool exitWithKeyboard = true); void enable(); void disable(); diff --git a/ion/src/device/flasher/main.cpp b/ion/src/device/flasher/main.cpp index 710238ff6..3dcfcd80d 100644 --- a/ion/src/device/flasher/main.cpp +++ b/ion/src/device/flasher/main.cpp @@ -11,6 +11,6 @@ void ion_main(int argc, const char * const argv[]) { Ion::USB::enable(); while (!Ion::USB::isEnumerated()) { } - Ion::Device::USB::Calculator::PollAndReset(false); + Ion::USB::DFU(false); } } diff --git a/ion/src/device/n0100/drivers/config/console.h b/ion/src/device/n0100/drivers/config/console.h index 49df3c541..33c122577 100644 --- a/ion/src/device/n0100/drivers/config/console.h +++ b/ion/src/device/n0100/drivers/config/console.h @@ -13,7 +13,7 @@ using namespace Regs; constexpr static USART Port = USART(3); constexpr static GPIOPin RxPin = GPIOPin(GPIOC, 11); constexpr static GPIOPin TxPin = GPIOPin(GPIOD, 8); -constexpr static GPIO::AFR::AlternateFunction AlternateFunction = GPIO::AFR::AlternateFunction::AF8; +constexpr static GPIO::AFR::AlternateFunction AlternateFunction = GPIO::AFR::AlternateFunction::AF7; /* The baud rate of the UART is set by the following equation: * BaudRate = f/USARTDIV, where f is the clock frequency and USARTDIV a divider. diff --git a/ion/src/device/shared/usb/dfu_relocated.cpp b/ion/src/device/shared/usb/dfu_relocated.cpp index 470ba2615..82868a88c 100644 --- a/ion/src/device/shared/usb/dfu_relocated.cpp +++ b/ion/src/device/shared/usb/dfu_relocated.cpp @@ -13,7 +13,7 @@ namespace USB { typedef void (*PollFunctionPointer)(bool exitWithKeyboard); -void DFU() { +void DFU(bool exitWithKeyboard) { /* DFU transfers can serve two purposes: * - Transfering RAM data between the machine and a host, e.g. Python scripts @@ -74,7 +74,7 @@ void DFU() { * add-symbol-file ion/src/device/usb/dfu.elf 0x20038000 */ - dfu_bootloader_entry(true); + dfu_bootloader_entry(exitWithKeyboard); /* 5- Restore interrupts */ Device::Timing::init(); diff --git a/ion/src/device/shared/usb/dfu_xip.cpp b/ion/src/device/shared/usb/dfu_xip.cpp index 5eedcd6b6..753948f73 100644 --- a/ion/src/device/shared/usb/dfu_xip.cpp +++ b/ion/src/device/shared/usb/dfu_xip.cpp @@ -3,8 +3,8 @@ namespace Ion { namespace USB { -void DFU() { - Ion::Device::USB::Calculator::PollAndReset(true); +void DFU(bool exitWithKeyboard) { + Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard); } } diff --git a/ion/src/simulator/shared/dummy/usb.cpp b/ion/src/simulator/shared/dummy/usb.cpp index 0b1c8c244..b118ae964 100644 --- a/ion/src/simulator/shared/dummy/usb.cpp +++ b/ion/src/simulator/shared/dummy/usb.cpp @@ -11,7 +11,7 @@ bool Ion::USB::isEnumerated() { void Ion::USB::clearEnumerationInterrupt() { } -void Ion::USB::DFU() { +void Ion::USB::DFU(bool) { } void Ion::USB::enable() { diff --git a/ion/src/simulator/shared/main_headless.cpp b/ion/src/simulator/shared/main_headless.cpp index d4e96e181..8b8fec39c 100644 --- a/ion/src/simulator/shared/main_headless.cpp +++ b/ion/src/simulator/shared/main_headless.cpp @@ -15,7 +15,9 @@ constexpr int kHeapSize = 131072; #ifdef NDEBUG -constexpr int kStackSize = 32768; +/* TODO : Reduce stack memory cost in prime factorization to allow running + * tests with the actual stack size */ +constexpr int kStackSize = 32768*10; #else constexpr int kStackSize = 32768*10; // In DEBUG mode, we increase the stack to be able to pass the tests #endif diff --git a/python/port/mpconfigport.h b/python/port/mpconfigport.h index 20024c5b7..a55925d82 100644 --- a/python/port/mpconfigport.h +++ b/python/port/mpconfigport.h @@ -105,6 +105,9 @@ // Whether to include: randrange, randint, choice, random, uniform #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +// Whether to support rounding of integers (incl bignum); eg round(123,-1)=120 +#define MICROPY_PY_BUILTINS_ROUND_INT (1) + // Function to seed URANDOM with on init #define MICROPY_PY_URANDOM_SEED_INIT_FUNC micropython_port_random()