Block type: bitwiseOperator
| Parameter | Description |
|---|---|
| input ports | 1 input port(s) declared. |
| Parameter | Description |
|---|---|
| output ports | 1 output port(s) declared. |
Bit-wise AND/OR/XOR/NAND/NOR/NOT of the input against a constant BitMask.
| Module | nflow_blocks |
| Library | Logic / Bit Operations |
| Type | bitwiseOperator |
| Label | Bitwise Operator |
Description
Reinterprets the (integer-valued) input as an NumBits-wide unsigned integer and applies the selected bit-wise Operation against the constant BitMask. NOT ignores the mask. The result is masked back to NumBits bits and returned as a double. Element-wise over the input width.
Ports
Input(s)
| Port | Role | Side | Position |
|---|---|---|---|
| Port_1 | Numeric signal read by the block. | left | x=0, y=40 |
Output(s)
| Port | Role | Side | Position |
|---|---|---|---|
| Port_1 | Numeric signal produced by the block. | right | x=90, y=40 |
Parameters
| Parameter | Default value |
|---|---|
Operation |
AND |
BitMask |
0 |
NumBits |
32 |
Block Characteristics
| Block type | bitwiseOperator |
| Family | Logic / Bit Operations |
| Rendered size | 90 x 80 |
| Phases | ALGEBRAIC |
| Internal state or history | no |
| Signal data type | double numeric values |
Algorithms
Equation or Rule
$$y = (u \star \text{BitMask}) \,\&\, (2^{\text{NumBits}}-1)$$Extended Capabilities
Code generation: supported for C and Rust.
Implementation Sources
modules/nflow_blocks/libraries/logic/library.json{
"id": "builtin.logic",
"title": "Logic / Bit Operations",
"version": "0.1.0",
"format": "nflow-2",
"builtin": true,
"blocks": [
{
"type": "and",
"label": "AND",
"icon": "and.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "and-math",
"formula": "\\text{AND}",
"textSize": "16px"
}
},
{
"type": "or",
"label": "OR",
"icon": "or.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "or-math",
"formula": "\\text{OR}",
"textSize": "16px"
}
},
{
"type": "xor",
"label": "XOR",
"icon": "xor.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "xor-math",
"formula": "\\text{XOR}",
"textSize": "16px"
}
},
{
"type": "not",
"label": "NOT",
"icon": "not.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "not-math",
"formula": "\\text{NOT}",
"textSize": "16px"
}
},
{
"type": "compareToConstant",
"label": "Compare Const",
"icon": "compareToConstant.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"relop": "ge",
"const": 0
},
"render": {
"type": "image",
"src": "exports/compareToConstant.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "intervalTest",
"label": "Interval Test",
"icon": "intervalTest.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"LowerLimit": 0,
"UpperLimit": 1,
"IntervalClosedLeft": 1,
"IntervalClosedRight": 1
},
"render": {
"type": "image",
"src": "exports/intervalTest.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "intervalTestDynamic",
"label": "Interval Test Dynamic",
"icon": "intervalTestDynamic.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 40,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"IntervalClosedLeft": 1,
"IntervalClosedRight": 1
},
"render": {
"type": "image",
"src": "exports/intervalTestDynamic.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "bitwiseOperator",
"label": "Bitwise Operator",
"icon": "bitwiseOperator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"Operation": "AND",
"BitMask": 0,
"NumBits": 32
},
"render": {
"type": "image",
"src": "exports/bitwiseOperator.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "bitSet",
"label": "Bit Set",
"icon": "bitSet.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"BitIndex": 0,
"NumBits": 32
},
"render": {
"type": "image",
"src": "exports/bitSet.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 80,
"height": 80
}
},
{
"type": "bitClear",
"label": "Bit Clear",
"icon": "bitClear.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"BitIndex": 0,
"NumBits": 32
},
"render": {
"type": "image",
"src": "exports/bitClear.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 80,
"height": 80
}
},
{
"type": "extractBits",
"label": "Extract Bits",
"icon": "extractBits.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"StartBit": 0,
"NumBitsToExtract": 8,
"NumBits": 32,
"OutputScaling": "keepWeight"
},
"render": {
"type": "image",
"src": "exports/extractBits.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "shiftArithmetic",
"label": "Shift Arithmetic",
"icon": "shiftArithmetic.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"ShiftDirection": "Left",
"ShiftNumber": 1
},
"render": {
"type": "image",
"src": "exports/shiftArithmetic.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "combinatorialLogic",
"label": "Combinatorial Logic",
"icon": "combinatorialLogic.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"TruthTable": [
0,
1,
1,
0
]
},
"render": {
"type": "image",
"src": "exports/combinatorialLogic.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "compareToZero",
"label": "Compare Zero",
"icon": "compareToZero.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"relop": "ne"
},
"render": {
"type": "image",
"src": "exports/compareToZero.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "relationalOperator",
"label": "Relational",
"icon": "relationalOperator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
},
{
"x": 0,
"y": 50,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"Operator": "ge"
},
"render": {
"type": "image",
"src": "exports/relationalOperator.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "switchCase",
"label": "Switch Case",
"icon": "switchCase.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 30,
"side": "right"
},
{
"x": 100,
"y": 50,
"side": "right"
}
],
"defaultParams": {
"CaseConditions": "{1}",
"ShowDefaultCase": "on"
},
"render": {
"type": "image",
"src": "exports/switchCase.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "if",
"label": "If",
"icon": "if.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
},
{
"x": 0,
"y": 50,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 30,
"side": "right"
},
{
"x": 100,
"y": 50,
"side": "right"
}
],
"defaultParams": {
"IfExpression": "u1 > 0",
"ElseIfExpressions": "",
"ShowElse": "on"
},
"render": {
"type": "image",
"src": "exports/if.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "logicalOperator",
"label": "Logical Operator",
"icon": "logicalOperator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
},
{
"x": 0,
"y": 50,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"Operator": "AND"
},
"render": {
"type": "image",
"src": "exports/logicalOperator.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 80,
"height": 80
}
}
]
}
modules/nflow_blocks/src/cpp/logic/bitwiseOperator.cpp//=============================================================================
// Copyright (c) 2016-present Allan CORNET (Nelson)
//=============================================================================
// This file is part of Nelson.
//=============================================================================
// LICENCE_BLOCK_BEGIN
// SPDX-License-Identifier: LGPL-3.0-or-later
// LICENCE_BLOCK_END
//=============================================================================
// bitwiseOperator: bit-wise logic between the (integer-valued) input and a
// constant BitMask (Bitwise Operator, mask form). Operation is one of AND, OR,
// XOR, NAND, NOR, NOT (NOT ignores the mask). Values are reinterpreted as
// unsigned integers of NumBits width (default 32); the result is masked back to
// that width and returned as a double. Pure algebraic feedthrough, element-wise
// over the input width. C / Rust code generation.
//=============================================================================
#include "SimEngineTypes.hpp"
#include "BlockRegistry.hpp"
#include "FieldNames.hpp"
#include "NFlowBlockDescriptor.hpp"
#include "NFlowCodegenHelpers.hpp"
#include <cmath>
#include <cstdint>
#include <string>
#include "logic_blocks.hpp"
//=============================================================================
namespace Nelson {
namespace NFlow {
//=============================================================================
// Operation codes: 0 AND, 1 OR, 2 XOR, 3 NAND, 4 NOR, 5 NOT.
static int
bwOp(const nflow::BlockDescriptor& bd)
{
const std::string s = bd.paramStr("Operation", "AND");
if (s == "OR") {
return 1;
}
if (s == "XOR") {
return 2;
}
if (s == "NAND") {
return 3;
}
if (s == "NOR") {
return 4;
}
if (s == "NOT") {
return 5;
}
return 0;
}
//=============================================================================
static uint64_t
bwFullMask(const nflow::BlockDescriptor& bd)
{
double nb = std::floor(bd.paramDouble("NumBits", 32.0));
if (nb < 1.0) {
nb = 1.0;
}
if (nb >= 64.0) {
return ~0ULL;
}
return (1ULL << static_cast<unsigned>(nb)) - 1ULL;
}
//=============================================================================
static uint64_t
bwApply(int op, uint64_t x, uint64_t m, uint64_t fm)
{
switch (op) {
case 1:
return (x | m) & fm;
case 2:
return (x ^ m) & fm;
case 3:
return (~(x & m)) & fm;
case 4:
return (~(x | m)) & fm;
case 5:
return (~x) & fm;
case 0:
default:
return (x & m) & fm;
}
}
//=============================================================================
bool
handleBitwiseOperator(SimCtx& ctx, const Block& b, Phase phase)
{
if (phase != Phase::ALGEBRAIC) {
return false;
}
if (!hasInput(ctx, b.nid, 0)) {
return false;
}
nflow::BlockDescriptor bd(b, ctx.variables);
const int op = bwOp(bd);
const uint64_t fm = bwFullMask(bd);
const uint64_t mask
= static_cast<uint64_t>(std::llround(bd.paramDouble("BitMask", 0.0))) & fm;
SigView u = getInputSig(ctx, b.nid, 0);
return emitElementwise(ctx, b.nid, [&](int i) -> double {
const uint64_t x = static_cast<uint64_t>(std::llround(sigAt(u, i))) & fm;
return static_cast<double>(bwApply(op, x, mask, fm));
});
}
//=============================================================================
static std::string
bwExprC(int op, const std::string& xu, const std::string& m, const std::string& fm)
{
switch (op) {
case 1:
return "(double)((" + xu + " | " + m + ") & " + fm + ")";
case 2:
return "(double)((" + xu + " ^ " + m + ") & " + fm + ")";
case 3:
return "(double)((~(" + xu + " & " + m + ")) & " + fm + ")";
case 4:
return "(double)((~(" + xu + " | " + m + ")) & " + fm + ")";
case 5:
return "(double)((~" + xu + ") & " + fm + ")";
case 0:
default:
return "(double)((" + xu + " & " + m + ") & " + fm + ")";
}
}
//=============================================================================
BlockCodegenTemplate
getCodeGenCBitwiseOperator()
{
BlockCodegenTemplate t;
t.emitStep = [](const BlockCodegenArgs& a) {
nflow::BlockDescriptor bd(*a.block, *a.variables);
const int op = bwOp(bd);
const uint64_t fm = bwFullMask(bd);
const uint64_t mask
= static_cast<uint64_t>(std::llround(bd.paramDouble("BitMask", 0.0))) & fm;
const std::string xu = "((unsigned long long)llround(" + a.in[0] + "))";
const std::string m = std::to_string(mask) + "ULL";
const std::string fms = std::to_string(fm) + "ULL";
a.line("out_" + a.id + " = " + bwExprC(op, xu, m, fms) + ";");
};
return t;
}
//=============================================================================
static std::string
bwExprRust(int op, const std::string& xu, const std::string& m, const std::string& fm)
{
switch (op) {
case 1:
return "(((" + xu + " | " + m + ") & " + fm + ") as f64)";
case 2:
return "(((" + xu + " ^ " + m + ") & " + fm + ") as f64)";
case 3:
return "(((!(" + xu + " & " + m + ")) & " + fm + ") as f64)";
case 4:
return "(((!(" + xu + " | " + m + ")) & " + fm + ") as f64)";
case 5:
return "(((!" + xu + ") & " + fm + ") as f64)";
case 0:
default:
return "(((" + xu + " & " + m + ") & " + fm + ") as f64)";
}
}
//=============================================================================
BlockCodegenTemplate
getCodeGenRustBitwiseOperator()
{
BlockCodegenTemplate t;
t.emitStep = [](const BlockCodegenArgs& a) {
nflow::BlockDescriptor bd(*a.block, *a.variables);
const int op = bwOp(bd);
const uint64_t fm = bwFullMask(bd);
const uint64_t mask
= static_cast<uint64_t>(std::llround(bd.paramDouble("BitMask", 0.0))) & fm;
// Wrap negatives via i64 -> u64 (two's complement), matching the
// interpreter and the C backend; a bare `as u64` saturates negative
// inputs to 0 in Rust.
const std::string xu = "((libm::round(" + a.in[0] + ") as i64) as u64)";
const std::string m = std::to_string(mask) + "u64";
const std::string fms = std::to_string(fm) + "u64";
a.line("out_" + a.id + " = " + bwExprRust(op, xu, m, fms) + ";");
};
return t;
}
//=============================================================================
} // namespace NFlow
} // namespace Nelson
//=============================================================================
d.blocks={ struct('id','c','type','constant','inputs',0,'outputs',1,'params',struct('Value',12)), struct('id','b','type','bitwiseOperator','inputs',1,'outputs',1,'params',struct('Operation','AND','BitMask',10,'NumBits',8)), struct('id','w','type','toWorkspace','inputs',1,'outputs',0,'params',struct('VariableName','y','SaveFormat','Array')) };
d.connections={ struct('from','c','to','b','fromIndex',0,'toIndex',0), struct('from','b','to','w','fromIndex',0,'toIndex',0) };
d.sampleTime=0.1; d.duration=0.2; d.solver='discrete'; d.variables=struct();
r=jsondecode(__nflow_simulate__(jsonencode(d)));
| Version | Description |
|---|---|
| 1.0.0 | initial version |