Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a074e0f
Model divmod outputs with generic truth tables
nanocoh Aug 6, 2026
5a5511b
Merge branch 'main' of https://github.com/nanocoh/naja
nanocoh Aug 6, 2026
487503c
Add Python sequential model API
nanocoh Aug 8, 2026
be29316
Add instance-parameterized truth tables
nanocoh Aug 8, 2026
abe47a6
Share Naja runtime with Python extension in Bazel
nanocoh Aug 9, 2026
d481fee
Fix Bazel Python test runtime loading
nanocoh Aug 10, 2026
9080f2b
Link TBB into Bazel shared runtime
nanocoh Aug 10, 2026
d3a6a7c
Merge branch 'main' into main
nanocoh Aug 10, 2026
b540895
Merge branch 'najaeda:main' into main
nanocoh Aug 13, 2026
253a669
Keep mixed FF/latch Liberty cells loadable
nanocoh Aug 13, 2026
c96e1b1
Merge branch 'main' of https://github.com/nanocoh/naja
nanocoh Aug 13, 2026
429dfe0
Merge branch 'najaeda:main' into main
nanocoh Aug 21, 2026
cfd3ae0
Fix Liberty state cells getting constant truth tables
nanocoh Aug 26, 2026
21c56f0
Preserve timing for opaque Liberty state cells
nanocoh Aug 26, 2026
e06b464
Merge branch 'najaeda:main' into main
nanocoh Aug 27, 2026
11f79b7
Merge branch 'najaeda:main' into main
nanocoh Sep 1, 2026
2175bff
test: improve Naja API coverage
nanocoh Sep 1, 2026
5f852b5
test: cover remaining modeling branches
nanocoh Sep 1, 2026
eb2f6da
Merge branch 'najaeda:main' into main
nanocoh Sep 4, 2026
83be8a9
Merge branch 'najaeda:main' into main
nanocoh Sep 7, 2026
1d33c24
Add shared Python runtime SDK and safe parameter lifetimes
nanocoh Sep 10, 2026
d3b5498
Merge branch 'najaeda:main' into main
nanocoh Sep 25, 2026
38a7a4c
Merge branch 'najaeda:main' into main
nanocoh Sep 28, 2026
caad1f8
Merge branch 'fix7' into main
nanocoh Sep 28, 2026
6331960
Keep the upstream 0.7.26 version for the SDK provider
nanocoh Sep 28, 2026
20574de
Merge branch 'najaeda:main' into main
nanocoh Sep 30, 2026
bca0448
Do not infer a memory for an array written from several always blocks
nanocoh Sep 30, 2026
c925b7d
Reduce the Python shared-runtime SDK to the shipped headers
nanocoh Oct 4, 2026
ad34893
Merge branch 'najaeda:main' into main
nanocoh Oct 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ endif()
include(GNUInstallDirs)

option(BUILD_NAJA_PYTHON "Build Naja Python package" OFF)
set(NAJA_EMBEDDED_LIBRARY_TYPE STATIC)
if(BUILD_NAJA_PYTHON AND WIN32)
# Put these objects in their owning DLL's input list so CMake's automatic
# export generation includes SDK entry points from the embedded components.
set(NAJA_EMBEDDED_LIBRARY_TYPE OBJECT)
endif()
if(BUILD_NAJA_PYTHON AND WIN32)
# Make imported targets from find_package() globally visible so parent
# directories can consume fmt exported by thirdparty/slang.
Expand Down
2 changes: 1 addition & 1 deletion src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ set(NAJA_CORE_HEADERS

list(APPEND NAJA_CORE_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/NajaVersion.h)

add_library(naja_core STATIC ${SOURCES})
add_library(naja_core ${NAJA_EMBEDDED_LIBRARY_TYPE} ${SOURCES})
set_property(TARGET naja_core PROPERTY POSITION_INDEPENDENT_CODE ON)
#target_link_libraries(naja_core PUBLIC cpptrace::cpptrace coverage_config sanitizers_config)
target_link_libraries(naja_core PUBLIC coverage_config sanitizers_config naja_fmt)
Expand Down
18 changes: 18 additions & 0 deletions src/najaeda/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
# SPDX-License-Identifier: Apache-2.0

if(BUILD_NAJA_PYTHON)
# Ship the headers matching this wheel's libraries so native consumers can
# link the installed runtime instead of building a second copy of Naja.
install(DIRECTORY ${PROJECT_SOURCE_DIR}/src/
DESTINATION najaeda/sdk/include/naja
FILES_MATCHING PATTERN "*.h")
install(FILES ${PROJECT_BINARY_DIR}/src/core/NajaVersion.h
DESTINATION najaeda/sdk/include/naja/core)
install(FILES
${PROJECT_SOURCE_DIR}/thirdparty/naja-verilog/src/VerilogConstructor.h
${PROJECT_SOURCE_DIR}/thirdparty/naja-verilog/src/VerilogTypes.h
DESTINATION najaeda/sdk/include/naja/verilog)
install(DIRECTORY ${SPDLOG_DIR}/spdlog ${ARGPARSE_DIR}/argparse
DESTINATION najaeda/sdk/include)
if(WIN32)
foreach(runtime IN ITEMS naja_nl naja_dnl naja_bne naja_opt naja_metrics naja_python)
install(FILES "$<TARGET_LINKER_FILE:${runtime}>" DESTINATION najaeda/sdk/lib)
endforeach()
endif()
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/najaeda
DESTINATION .
PATTERN "*~" EXCLUDE
Expand Down
2 changes: 1 addition & 1 deletion src/nl/formats/liberty/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ set(SOURCES

SET(HEADERS SNLLibertyConstructor.h)

add_library(naja_snl_liberty STATIC ${SOURCES})
add_library(naja_snl_liberty ${NAJA_EMBEDDED_LIBRARY_TYPE} ${SOURCES})
set_property(TARGET naja_snl_liberty PROPERTY POSITION_INDEPENDENT_CODE ON)

target_link_libraries(naja_snl_liberty
Expand Down
2 changes: 1 addition & 1 deletion src/nl/formats/systemverilog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ set(HEADERS
frontend/SNLSVIntent.h
)

add_library(naja_snl_systemverilog STATIC ${SOURCES})
add_library(naja_snl_systemverilog ${NAJA_EMBEDDED_LIBRARY_TYPE} ${SOURCES})
set_property(TARGET naja_snl_systemverilog PROPERTY POSITION_INDEPENDENT_CODE ON)

add_library(naja_snl_systemverilog_test STATIC EXCLUDE_FROM_ALL ${SOURCES})
Expand Down
43 changes: 39 additions & 4 deletions src/nl/formats/systemverilog/frontend/SNLSVConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7450,25 +7450,60 @@ endmodule
}
}

for (const auto* seqBlock : sequentialBlocks) {
const Statement* candidateStmt = &seqBlock->getBody();
// An inferred memory is owned by one sequential block. A symbol written
// from several blocks (for example one always block per element in a
// generate loop) would leave the other writers to the generic lowering,
// which then drives the memory's read data a second time. Such a symbol
// is left to the generic lowering for all of its writers.
const auto collectDirectCandidates =
[&](const slang::ast::ProceduralBlockSymbol& seqBlock,
std::vector<const slang::ast::ValueSymbol*>& candidates) {
const Statement* candidateStmt = &seqBlock.getBody();
if (const auto* timed = findTimedStatement(*candidateStmt)) {
candidateStmt = &timed->stmt;
}
candidateStmt = candidateStmt ? unwrapStatement(*candidateStmt) : nullptr;
if (!candidateStmt) {
continue; // LCOV_EXCL_LINE
return; // LCOV_EXCL_LINE
}
std::vector<const slang::ast::ValueSymbol*> candidates;
std::unordered_set<const slang::ast::ValueSymbol*> seenCandidates;
collectDirectSequentialMemoryCandidates(
*candidateStmt,
candidates,
seenCandidates);
};
std::unordered_map<const slang::ast::ValueSymbol*, size_t> directWriterBlocks;
for (const auto* seqBlock : sequentialBlocks) {
std::vector<const slang::ast::ValueSymbol*> candidates;
collectDirectCandidates(*seqBlock, candidates);
for (const auto* stateSymbol : candidates) {
if (stateSymbol) {
++directWriterBlocks[stateSymbol];
}
}
}

for (const auto* seqBlock : sequentialBlocks) {
std::vector<const slang::ast::ValueSymbol*> candidates;
collectDirectCandidates(*seqBlock, candidates);
for (const auto* stateSymbol : candidates) {
if (!stateSymbol || inferredMemoryByStateSymbol_.contains(stateSymbol)) {
continue;
}
if (directWriterBlocks[stateSymbol] > 1) {
if (warnedUninferredMemorySymbols_.insert(stateSymbol).second) {
std::ostringstream reason;
reason << "Memory '" << std::string(stateSymbol->name)
<< "' was not inferred as naja_mem: written from "
<< directWriterBlocks[stateSymbol]
<< " sequential blocks; using generic sequential lowering";
reportWarning(
"uninferred_memory_generic_sequential_lowering",
reason.str(),
getSourceRange(*stateSymbol));
}
continue;
}
InferredMemory memory;
std::string failureReason;
if (!tryMatchDirectSequentialMemoryBlock(
Expand Down
2 changes: 1 addition & 1 deletion src/nl/formats/verilog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ SET(HEADERS
backend/SNLVRLDumper.h
)

add_library(naja_snl_verilog STATIC ${SOURCES})
add_library(naja_snl_verilog ${NAJA_EMBEDDED_LIBRARY_TYPE} ${SOURCES})
set_property(TARGET naja_snl_verilog PROPERTY POSITION_INDEPENDENT_CODE ON)

target_link_libraries(naja_snl_verilog naja_verilog naja_nl)
Expand Down
2 changes: 1 addition & 1 deletion src/nl/netlist/serialization/capnp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ capnp_generate_cpp(najaCommonSources najaCommonHeaders ${CAPNPC_SRC_PREFIX}/naja
capnp_generate_cpp(najaNLInterfaceSources najaNLInterfaceHeaders ${CAPNPC_SRC_PREFIX}/naja_nl_interface.capnp)
capnp_generate_cpp(najaNLImplementationSources najaNLImplementationHeaders ${CAPNPC_SRC_PREFIX}/naja_nl_implementation.capnp)

add_library(naja_nl_dump STATIC
add_library(naja_nl_dump ${NAJA_EMBEDDED_LIBRARY_TYPE}
${najaCommonSources} ${najaNLInterfaceSources}
${najaNLImplementationSources}
SNLDump.cpp SNLDumpManifest.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/nl/netlist/snl/SNLInstParameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ void SNLInstParameter::preDestroy() {
}

void SNLInstParameter::destroyFromInstance() {
// The owning instance is already clearing its parameter collection. Still
// release properties so retained Python wrappers are invalidated.
super::preDestroy();
delete this;
}

Expand Down
6 changes: 5 additions & 1 deletion src/nl/netlist/snl/SNLParameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ void SNLParameter::preCreate(SNLDesign* design, const NLName& name) {

void SNLParameter::destroy() {
design_->removeParameter(this);
NajaObject::preDestroy();
delete this;
}

void SNLParameter::destroyFromDesign() {
// Notify property owners before freeing the parameter, including Python
// wrappers that may outlive the design or universe.
NajaObject::preDestroy();
delete this;
}

Expand Down Expand Up @@ -107,4 +111,4 @@ std::string SNLParameter::getDescription() const {
}
//LCOV_EXCL_STOP

} // namespace naja::NL
} // namespace naja::NL
2 changes: 1 addition & 1 deletion src/nl/netlist/visual/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set(NETLIST_GRAPH_HEADERS
NetlistGraph.h NetlistGraph_impl.h
)

add_library(naja_snl_visual STATIC ${NETLIST_GRAPH_SOURCES})
add_library(naja_snl_visual ${NAJA_EMBEDDED_LIBRARY_TYPE} ${NETLIST_GRAPH_SOURCES})
set_property(TARGET naja_snl_visual PROPERTY POSITION_INDEPENDENT_CODE ON)

target_link_libraries(naja_snl_visual PUBLIC naja_nl)
Expand Down
57 changes: 57 additions & 0 deletions test/nl/python/naja_wrapping/test_parameter_lifetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# SPDX-FileCopyrightText: 2026 The Naja authors
# SPDX-License-Identifier: Apache-2.0

import gc
import unittest

import naja


class ParameterLifetimeTests(unittest.TestCase):
def setUp(self):
self.universe = naja.NLUniverse.create()
library = naja.NLLibrary.create(naja.NLDB.create(self.universe))
self.model = naja.SNLDesign.create(library, "model")
self.parameter = naja.SNLParameter.create_string(self.model, "INIT", "x")
self.top = naja.SNLDesign.create(library, "top")
self.instance = naja.SNLInstance.create(self.top, self.model, "i")
self.inst_parameter = naja.SNLInstParameter.create(
self.instance, self.parameter, "y")

def tearDown(self):
if naja.NLUniverse.get() is not None:
naja.NLUniverse.get().destroy()

def test_universe_destruction_invalidates_retained_parameter_wrappers(self):
self.universe.destroy()
with self.assertRaisesRegex(RuntimeError, "unbound"):
self.parameter.getName()
with self.assertRaisesRegex(RuntimeError, "unbound"):
self.inst_parameter.getValue()
# Used to dereference freed objects and abort from the deallocator.
self.parameter = self.inst_parameter = None
gc.collect()

def test_instance_destruction_invalidates_only_its_parameter_wrapper(self):
self.instance.destroy()
with self.assertRaisesRegex(RuntimeError, "unbound"):
self.inst_parameter.getValue()
self.assertEqual("INIT", self.parameter.getName())
self.inst_parameter = None
gc.collect()

def test_design_destruction_invalidates_parameter_wrapper(self):
self.instance.destroy()
self.model.destroy()
with self.assertRaisesRegex(RuntimeError, "unbound"):
self.parameter.getName()

def test_explicit_parameter_destruction_invalidates_wrapper(self):
self.instance.destroy()
self.parameter.destroy()
with self.assertRaisesRegex(RuntimeError, "unbound"):
self.parameter.getName()


if __name__ == "__main__":
unittest.main()
Loading