Block type: busAssignment
| Parameter | Description |
|---|---|
| input ports | 2 input port(s) declared. |
| Parameter | Description |
|---|---|
| output ports | 1 output port(s) declared. |
Replaces selected members of a bus, passing the rest through unchanged.
| Module | nflow_blocks |
| Library | Utility |
| Type | busAssignment |
| Label | Bus Assignment |
Description
Replaces selected members of a bus and passes the rest through (Bus Assignment). Port 0 is the base bus; its type sets the output bus type; ports 1..N carry replacement signals, one per path in AssignedSignals. The output is a bus of the same type: a full copy of the base with each assigned member's packed region overwritten by the matching replacement input. The mirror image of busSelector; all storage lanes (real / imaginary / int64) are preserved. Simulation-only, like the other bus-routing blocks.
Ports
Input(s)
| Port | Role | Side | Position |
|---|---|---|---|
| Port_1 | Numeric signal read by the block. | left | x=0, y=20 |
| Port_2 | 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=30 |
Parameters
| Parameter | Default value |
|---|---|
AssignedSignals |
[] |
Block Characteristics
| Block type | busAssignment |
| Family | Utility |
| Rendered size | 90 x 60 |
| Phases | ALGEBRAIC |
| Internal state or history | no |
| Signal data type | double numeric values |
Algorithms
Extended Capabilities
Code generation: supported for C and Rust.
Implementation Sources
modules/nflow_blocks/libraries/utility/library.json{
"id": "builtin.utility",
"title": "Utility",
"version": "1.0.0",
"format": "nflow-2",
"metadata": {
"author": "Allan CORNET",
"created": "2026-03-21",
"tool": "Nelson nflow"
},
"comment": "Utility blocks such as switches, comments, and subsystems",
"license": "LGPL-3.0",
"builtin": true,
"blocks": [
{
"type": "comment",
"label": "Comment",
"icon": "comment.svg",
"phases": [],
"width": 220,
"height": 120,
"inputs": [],
"outputs": [],
"defaultParams": {
"CommentText": "",
"ShowBorder": true
},
"render": {
"type": "comment",
"bodyClass": "block-body"
}
},
{
"type": "switch",
"label": "Switch",
"icon": "switch.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 0,
"side": "left"
},
{
"x": 0,
"y": 40,
"side": "left"
},
{
"x": 0,
"y": 80,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"Criteria": "ge",
"Threshold": 0
},
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "switch-math"
}
},
{
"type": "multiportSwitch",
"label": "Multiport Switch",
"icon": "multiportSwitch.svg",
"phases": [
"ALGEBRAIC"
],
"width": 40,
"height": 80,
"inputs": [
{
"x": 20,
"y": 0,
"side": "top"
},
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 40,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 40,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"DataPortCount": 3
},
"render": {
"type": "image",
"src": "exports/multiportSwitch.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 40,
"height": 90
}
},
{
"type": "toggleSwitch",
"label": "Toggle Switch",
"icon": "toggleSwitch.svg",
"phases": [
"OUTPUT"
],
"width": 80,
"height": 50,
"inputs": [],
"outputs": [
{
"x": 80,
"y": 25,
"side": "right"
}
],
"defaultParams": {
"State": 0,
"OnLabel": "ON",
"OffLabel": "OFF",
"OnValue": 1,
"OffValue": 0
},
"render": {
"type": "toggle"
}
},
{
"type": "subsystem",
"icon": "subsystem.svg",
"label": "Subsystem",
"phases": [
"INIT",
"OUTPUT",
"ALGEBRAIC",
"UPDATE"
],
"width": 120,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 120,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"name": "Subsystem",
"externalInputs": [],
"externalOutputs": [],
"subsystem": null
},
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "subsystem-math",
"formula": "\\mathsf{Sub}"
}
},
{
"type": "mux",
"label": "Mux",
"icon": "mux.svg",
"phases": [
"OUTPUT"
],
"width": 8,
"height": 40,
"inputs": [
{
"x": 0,
"y": 10,
"side": "left"
},
{
"x": 0,
"y": 30,
"side": "left"
}
],
"outputs": [
{
"x": 8,
"y": 20,
"side": "right"
}
],
"defaultParams": {
"Inputs": 2
}
},
{
"type": "demux",
"label": "Demux",
"icon": "demux.svg",
"phases": [
"OUTPUT"
],
"width": 8,
"height": 40,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
}
],
"outputs": [
{
"x": 8,
"y": 10,
"side": "right"
},
{
"x": 8,
"y": 30,
"side": "right"
}
],
"defaultParams": {
"Outputs": 2
}
},
{
"type": "convert",
"label": "Convert",
"icon": "convert.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 50,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 25,
"side": "right"
}
],
"defaultParams": {
"OutDataType": "double",
"SaturateOnOverflow": true,
"Rounding": "nearest"
}
},
{
"type": "initialCondition",
"label": "IC",
"icon": "initialCondition.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 50,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 25,
"side": "right"
}
],
"defaultParams": {
"InitialValue": 0
}
},
{
"type": "dataStoreMemory",
"label": "Data Store Memory",
"icon": "dataStoreMemory.svg",
"phases": [
"INIT"
],
"width": 70,
"height": 60,
"inputs": [],
"outputs": [],
"defaultParams": {
"DataStoreName": "A",
"InitialValue": 0
},
"render": {
"type": "image",
"src": "exports/dataStoreMemory.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 70,
"height": 60
}
},
{
"type": "dataStoreWrite",
"label": "Data Store Write",
"icon": "dataStoreWrite.svg",
"phases": [
"INIT",
"UPDATE"
],
"width": 70,
"height": 60,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
}
],
"outputs": [],
"defaultParams": {
"DataStoreName": "A"
},
"render": {
"type": "image",
"src": "exports/dataStoreWrite.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 70,
"height": 60
}
},
{
"type": "dataStoreRead",
"label": "Data Store Read",
"icon": "dataStoreRead.svg",
"phases": [
"OUTPUT"
],
"width": 70,
"height": 60,
"inputs": [],
"outputs": [
{
"x": 70,
"y": 30,
"side": "right"
}
],
"defaultParams": {
"DataStoreName": "A"
},
"render": {
"type": "image",
"src": "exports/dataStoreRead.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 70,
"height": 60
}
},
{
"type": "selector",
"label": "Selector",
"icon": "selector.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 50,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 25,
"side": "right"
}
],
"defaultParams": {
"Indices": "1"
}
},
{
"type": "reshape",
"label": "Reshape",
"icon": "reshape.svg",
"phases": [
"INIT",
"ALGEBRAIC"
],
"width": 80,
"height": 50,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 25,
"side": "right"
}
],
"defaultParams": {
"OutputDimensions": ""
}
},
{
"type": "concatenate",
"label": "Concatenate",
"icon": "concatenate.svg",
"phases": [
"ALGEBRAIC"
],
"width": 60,
"height": 60,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 60,
"y": 30,
"side": "right"
}
],
"defaultParams": {
"ConcatenateDimension": 1
}
},
{
"type": "busCreator",
"label": "Bus Creator",
"icon": "busCreator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 70,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
},
{
"x": 0,
"y": 45,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 35,
"side": "right"
}
],
"defaultParams": {
"BusType": "",
"NonVirtual": false,
"MemberNames": []
}
},
{
"type": "busSelector",
"label": "Bus Selector",
"icon": "busSelector.svg",
"phases": [
"ALGEBRAIC"
],
"width": 85,
"height": 70,
"inputs": [
{
"x": 0,
"y": 35,
"side": "left"
}
],
"outputs": [
{
"x": 85,
"y": 25,
"side": "right"
},
{
"x": 85,
"y": 45,
"side": "right"
}
],
"defaultParams": {
"SelectedSignals": [],
"OutputAsBus": false
}
},
{
"type": "merge",
"label": "Merge",
"icon": "merge.svg",
"phases": [
"INIT",
"ALGEBRAIC"
],
"width": 40,
"height": 80,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
},
{
"x": 0,
"y": 50,
"side": "left"
}
],
"outputs": [
{
"x": 40,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"InitialOutput": 0
},
"render": {
"type": "image",
"src": "exports/merge.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 40,
"height": 80
}
},
{
"type": "functionCallGenerator",
"label": "Function-Call Generator",
"icon": "functionCallGenerator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 60,
"inputs": [],
"outputs": [
{
"x": 90,
"y": 30,
"side": "right"
}
],
"defaultParams": {
"NumberOfIterations": 1
},
"render": {
"type": "image",
"src": "exports/functionCallGenerator.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 60
}
},
{
"type": "functionCallSplit",
"label": "Function-Call Split",
"icon": "functionCallSplit.svg",
"phases": [],
"width": 60,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 60,
"y": 30,
"side": "right"
},
{
"x": 60,
"y": 50,
"side": "right"
}
],
"defaultParams": {},
"render": {
"type": "image",
"src": "exports/functionCallSplit.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 60,
"height": 80
}
},
{
"type": "iteratorNumber",
"label": "Iterator Number",
"icon": "iteratorNumber.svg",
"phases": [
"OUTPUT"
],
"width": 70,
"height": 50,
"inputs": [],
"outputs": [
{
"x": 70,
"y": 25,
"side": "right"
}
],
"defaultParams": {},
"render": {
"type": "image",
"src": "exports/iteratorNumber.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 70,
"height": 50
}
},
{
"type": "iteratorCondition",
"label": "Iterator Condition",
"icon": "iteratorCondition.svg",
"phases": [
"ALGEBRAIC"
],
"width": 70,
"height": 50,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
}
],
"outputs": [
{
"x": 70,
"y": 25,
"side": "right"
}
],
"defaultParams": {},
"render": {
"type": "image",
"src": "exports/iteratorCondition.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 70,
"height": 50
}
},
{
"type": "width",
"label": "Width",
"icon": "width.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 50,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 25,
"side": "right"
}
],
"defaultParams": {}
},
{
"type": "signalConversion",
"label": "Signal Conversion",
"icon": "signalConversion.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 50,
"inputs": [
{
"x": 0,
"y": 25,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 25,
"side": "right"
}
],
"defaultParams": {}
},
{
"type": "assignment",
"label": "Assignment",
"icon": "assignment.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 60,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 30,
"side": "right"
}
],
"defaultParams": {
"Indices": [
1
]
}
},
{
"type": "busAssignment",
"label": "Bus Assignment",
"icon": "busAssignment.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 60,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 30,
"side": "right"
}
],
"defaultParams": {
"AssignedSignals": []
}
}
]
}
modules/nflow_blocks/src/cpp/routing/busAssignment.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
//=============================================================================
// busAssignment: replaces selected members of a bus and passes the rest
// through. Port 0 is the base bus (its type sets the output bus type); ports
// 1..N carry replacement signals, one per path in AssignedSignals. The output
// is a bus of the same type: a full copy of the base, with each assigned
// member's packed region overwritten by the matching replacement input. The
// mirror image of busSelector; member offsets come from SimCtx::busRegistry and
// all storage lanes (real / imaginary / int64) are preserved.
//=============================================================================
#include "routing_blocks.hpp"
#include "SimEngineBusTypes.hpp"
#include "FieldNames.hpp"
#include "NFlowBlockDescriptor.hpp"
#include <algorithm>
#include <string>
#include <vector>
//=============================================================================
namespace {
// Assigned member paths from the AssignedSignals parameter (port k+1 replaces
// path k). Falls back to SelectedSignals so a diagram may reuse either key.
std::vector<std::string>
assignedPaths(const Nelson::NFlow::Block& b)
{
std::vector<std::string> paths;
const char* keys[] = { "AssignedSignals", "SelectedSignals" };
for (const char* key : keys) {
if (b.params.contains(key) && b.params[key].is_array()) {
for (const auto& s : b.params[key]) {
if (s.is_string()) {
paths.push_back(s.get<std::string>());
}
}
if (!paths.empty()) {
break;
}
}
}
return paths;
}
} // namespace
//=============================================================================
bool
Nelson::NFlow::handleBusAssignment(SimCtx& ctx, const Block& b, Phase phase)
{
if (phase != Phase::ALGEBRAIC) {
return false;
}
// The base bus id comes from port 0's signal descriptor.
int busId = -1;
if (ctx.inputSigs && ctx.sigs && b.nid < (int)ctx.inputSigs->size()) {
const auto& sIds = (*ctx.inputSigs)[b.nid];
if (!sIds.empty() && sIds[0] >= 0 && sIds[0] < (int)ctx.sigs->size()) {
busId = (*ctx.sigs)[sIds[0]].busTypeId;
}
}
if (busId < 0 || !ctx.busRegistry) {
return false;
}
SigView base = getInputSig(ctx, b.nid, 0);
double* y = outputSlice(ctx, b.nid, 0);
double* yim = outputSliceImag(ctx, b.nid, 0);
long long* yi = outputSliceI64(ctx, b.nid, 0);
const int outW = outputWidth(ctx, b.nid, 0);
bool changed = false;
// 1. Pass the whole base bus through (all lanes).
for (int k = 0; k < outW; ++k) {
const double val = sigAt(base, k);
if (y[k] != val) {
y[k] = val;
changed = true;
}
if (yim) {
const double iv = (base.imag && base.width > 0)
? ((base.width == 1) ? base.imag[0] : base.imag[k])
: 0.0;
if (yim[k] != iv) {
yim[k] = iv;
changed = true;
}
}
if (yi) {
const long long ev = base.idata ? ((base.width == 1) ? base.idata[0] : base.idata[k])
: static_cast<long long>(val);
if (yi[k] != ev) {
yi[k] = ev;
changed = true;
}
}
}
// 2. Overwrite each assigned member's region from its replacement port.
const std::vector<std::string> paths = assignedPaths(b);
for (size_t p = 0; p < paths.size(); ++p) {
const int off = ctx.busRegistry->memberOffset(busId, paths[p]);
const PortSig* ms = ctx.busRegistry->resolvePath(busId, paths[p]);
if (off < 0 || !ms) {
continue; // unknown path: rejected at compile time, defensive here
}
const int wo
= (ms->busTypeId >= 0) ? ctx.busRegistry->packedWidth(ms->busTypeId) : ms->width();
SigView rv = getInputSig(ctx, b.nid, static_cast<int>(p) + 1);
for (int k = 0; k < wo && off + k < outW; ++k) {
const double val = sigAt(rv, k);
if (y[off + k] != val) {
y[off + k] = val;
changed = true;
}
if (yim) {
const double iv
= (rv.imag && rv.width > 0) ? ((rv.width == 1) ? rv.imag[0] : rv.imag[k]) : 0.0;
if (yim[off + k] != iv) {
yim[off + k] = iv;
changed = true;
}
}
if (yi) {
const long long ev = rv.idata ? ((rv.width == 1) ? rv.idata[0] : rv.idata[k])
: static_cast<long long>(val);
if (yi[off + k] != ev) {
yi[off + k] = ev;
changed = true;
}
}
}
}
return changed;
}
//=============================================================================
bool
Nelson::NFlow::resolveBusAssignmentDims(const Block& /*b*/, const ValMap& /*vars*/,
const std::vector<PortSig>& inSigs, std::vector<PortSig>& outSigs, BusRegistry& /*busReg*/,
std::string& /*err*/)
{
// The output is the base bus (port 0) unchanged in shape/type; only the
// member values differ. Copy the input descriptor, keeping the output's own
// packed offset. Until the base bus resolves (first fixed-point pass) leave
// the output as-is.
const int busId = inSigs.empty() ? -1 : inSigs[0].busTypeId;
if (busId < 0 || outSigs.empty()) {
return true;
}
const int keepOffset = outSigs[0].offset;
outSigs[0] = inSigs[0];
outSigs[0].offset = keepOffset;
return true;
}
//=============================================================================
d.blocks={ struct('id','pos','type','constant','inputs',0,'outputs',1,'params',struct('Value',[1 2])), struct('id','cnt','type','constant','inputs',0,'outputs',1,'params',struct('Value',7)), struct('id','nc','type','constant','inputs',0,'outputs',1,'params',struct('Value',99)), struct('id','bc','type','busCreator','inputs',2,'outputs',1,'params',struct('MemberNames',{{'pos','count'}})), struct('id','ba','type','busAssignment','inputs',2,'outputs',1,'params',struct('AssignedSignals',{{'count'}})), struct('id','sel','type','busSelector','inputs',1,'outputs',1,'params',struct('SelectedSignals',{{'count'}})), struct('id','w','type','toWorkspace','inputs',1,'outputs',0,'params',struct('VariableName','y','SaveFormat','Array')) };
d.connections={ struct('from','pos','to','bc','fromIndex',0,'toIndex',0), struct('from','cnt','to','bc','fromIndex',0,'toIndex',1), struct('from','bc','to','ba','fromIndex',0,'toIndex',0), struct('from','nc','to','ba','fromIndex',0,'toIndex',1), struct('from','ba','to','sel','fromIndex',0,'toIndex',0), struct('from','sel','to','w','fromIndex',0,'toIndex',0) };
d.sampleTime=0.1; d.duration=0.1; d.solver='discrete'; d.variables=struct();
r=jsondecode(__nflow_simulate__(jsonencode(d)));
| Version | Description |
|---|---|
| 1.0.0 | initial version |