├── include ├── CMakeLists.txt └── circt-stream │ ├── Dialect │ ├── CMakeLists.txt │ └── Stream │ │ ├── CMakeLists.txt │ │ ├── StreamDialect.h │ │ ├── StreamTypes.h │ │ ├── StreamOps.h │ │ ├── StreamTypes.td │ │ ├── StreamDialect.td │ │ └── StreamOps.td │ ├── CMakeLists.txt │ ├── Conversion │ ├── CMakeLists.txt │ ├── Passes.h │ ├── StreamToHandshake.h │ └── Passes.td │ └── Transform │ ├── CMakeLists.txt │ ├── CustomBufferInsertion.h │ ├── Passes.h │ └── Passes.td ├── lib ├── Dialect │ ├── CMakeLists.txt │ └── Stream │ │ ├── CMakeLists.txt │ │ ├── StreamDialect.cpp │ │ └── StreamOps.cpp ├── Conversion │ ├── CMakeLists.txt │ └── StreamToHandshake │ │ └── CMakeLists.txt ├── CMakeLists.txt └── Transform │ ├── CMakeLists.txt │ └── CustomBufferInsertion.cpp ├── .clang-format ├── .gitmodules ├── test ├── standalone-opt │ └── commandline.mlir ├── Conversion │ └── StreamToHandshake │ │ ├── sink.mlir │ │ ├── noop.mlir │ │ ├── multiple-ops.mlir │ │ ├── filter.mlir │ │ ├── combine.mlir │ │ ├── pack_unpack.mlir │ │ ├── split.mlir │ │ ├── map.mlir │ │ ├── reduce.mlir │ │ ├── combined.mlir │ │ └── registers.mlir ├── CMakeLists.txt ├── Dialect │ └── Stream │ │ ├── reg-errors.mlir │ │ ├── canonicalize.mlir │ │ ├── reg-ops.mlir │ │ ├── ops.mlir │ │ └── errors.mlir ├── lit.site.cfg.py.in ├── lit.cfg.py └── Transform │ └── custom-buffer-insertion.mlir ├── .gitignore ├── integration_test ├── Dialect │ └── Stream │ │ ├── reduce │ │ ├── reduce.py │ │ └── reduce.mlir │ │ ├── complex │ │ ├── complex.py │ │ └── complex.mlir │ │ ├── reduce-with-reg │ │ ├── reduce-with-reg.py │ │ └── reduce-with-reg.mlir │ │ ├── projection │ │ ├── projection.py │ │ └── projection.mlir │ │ ├── task-pipelining │ │ ├── task-pipelining.py │ │ └── task-pipelining.mlir │ │ ├── combine │ │ ├── combine.py │ │ └── combine.mlir │ │ ├── combine-with-reg │ │ ├── combine-with-reg.py │ │ └── combine-with-reg.mlir │ │ ├── split │ │ ├── split.py │ │ └── split.mlir │ │ ├── split-with-reg │ │ ├── split-with-reg.py │ │ └── split-with-reg.mlir │ │ ├── map │ │ ├── map.py │ │ └── map.mlir │ │ ├── filter │ │ ├── filter.py │ │ └── filter.mlir │ │ ├── map-with-reg │ │ ├── map-with-reg.py │ │ └── map-with-reg.mlir │ │ ├── filter-with-reg │ │ ├── filter-with-reg.py │ │ └── filter-with-reg.mlir │ │ ├── lit.local.cfg.py │ │ ├── projection-with-reg │ │ ├── projection-with-reg.mlir │ │ └── projection-with-reg.py │ │ ├── projection-selection │ │ ├── projection-selection.mlir │ │ └── projection-selection.py │ │ ├── stream-stats │ │ ├── stream-stats.py │ │ └── stream-stats.mlir │ │ ├── cocotb_driver.py │ │ └── helper.py ├── CMakeLists.txt ├── lit.site.cfg.py.in └── lit.cfg.py ├── tools ├── CMakeLists.txt └── stream-opt.cpp ├── README.md ├── docs └── RationaleStream.md ├── CMakeLists.txt ├── .github └── workflows │ └── formatting.yml └── LICENSE.txt /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(circt-stream) 2 | -------------------------------------------------------------------------------- /lib/Dialect/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Stream) 2 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Stream) 2 | -------------------------------------------------------------------------------- /lib/Conversion/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(StreamToHandshake) 2 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AlwaysBreakTemplateDeclarations: Yes 3 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Conversion) 2 | add_subdirectory(Dialect) 3 | add_subdirectory(Transform) 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "circt"] 2 | path = circt 3 | url = https://github.com/llvm/circt.git 4 | branch = main 5 | -------------------------------------------------------------------------------- /include/circt-stream/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Conversion) 2 | add_subdirectory(Dialect) 3 | add_subdirectory(Transform) 4 | 5 | 6 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/Stream/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_mlir_dialect(StreamOps stream) 2 | add_mlir_doc(StreamOps StreamOps Stream/ -gen-op-doc) 3 | -------------------------------------------------------------------------------- /test/standalone-opt/commandline.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt --show-dialects | FileCheck %s 2 | // CHECK: Available Dialects: 3 | // CHECK: stream 4 | -------------------------------------------------------------------------------- /lib/Dialect/Stream/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_mlir_dialect_library(CIRCTStreamStream 2 | StreamDialect.cpp 3 | StreamOps.cpp 4 | 5 | DEPENDS 6 | MLIRStreamOpsIncGen 7 | 8 | LINK_LIBS PUBLIC 9 | MLIRIR 10 | ) 11 | -------------------------------------------------------------------------------- /include/circt-stream/Conversion/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LLVM_TARGET_DEFINITIONS Passes.td) 2 | mlir_tablegen(Passes.h.inc -gen-pass-decls -name Conversion) 3 | add_public_tablegen_target(CIRCTStreamConversionPassIncGen) 4 | 5 | add_mlir_doc(Passes CIRCTStreamConversionPasses ./ -gen-pass-doc) 6 | -------------------------------------------------------------------------------- /lib/Transform/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_mlir_library(CIRCTStreamTransform 2 | CustomBufferInsertion.cpp 3 | 4 | LINK_LIBS PUBLIC 5 | MLIRIR 6 | MLIRMemRefDialect 7 | MLIRFuncDialect 8 | MLIRSupport 9 | MLIRTransformUtils 10 | 11 | DEPENDS 12 | CIRCTStreamTransformPassIncGen 13 | ) 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | /install* 3 | /docker_build 4 | tags 5 | .vscode 6 | compile_commands.json 7 | .cache/ 8 | #* 9 | *~ 10 | .DS_Store 11 | __pycache__ 12 | lit.site.cfg.py 13 | 14 | # Pip artifacts 15 | /lib/Bindings/Python/build 16 | *.egg-info 17 | *.whl 18 | 19 | # External software 20 | /ext 21 | 22 | # tmp directories 23 | /tmp 24 | -------------------------------------------------------------------------------- /include/circt-stream/Transform/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LLVM_TARGET_DEFINITIONS Passes.td) 2 | mlir_tablegen(Passes.h.inc -gen-pass-decls -name Transform) 3 | mlir_tablegen(Transform.capi.h.inc -gen-pass-capi-header --prefix Transform) 4 | mlir_tablegen(Transform.capi.cpp.inc -gen-pass-capi-impl --prefix Transform) 5 | add_public_tablegen_target(CIRCTStreamTransformPassIncGen) 6 | -------------------------------------------------------------------------------- /lib/Conversion/StreamToHandshake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_mlir_library(CIRCTStreamStreamToHandshake 2 | StreamToHandshake.cpp 3 | 4 | DEPENDS 5 | CIRCTStreamConversionPassIncGen 6 | 7 | LINK_LIBS PUBLIC 8 | MLIRIR 9 | MLIRPass 10 | MLIRSupport 11 | MLIRTransforms 12 | MLIRControlFlowDialect 13 | MLIRFuncDialect 14 | 15 | CIRCTHandshake 16 | CIRCTStreamStream 17 | ) 18 | 19 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/reduce/reduce.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([6])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 15 | 16 | await out0Check 17 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/complex/complex.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([12])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([1, 2, 3])) 15 | 16 | await out0Check 17 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/reduce-with-reg/reduce-with-reg.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([36])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 15 | 16 | await out0Check 17 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/sink.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | 3 | module { 4 | func.func @sink(%in: !stream.stream) { 5 | stream.sink %in : !stream.stream 6 | return 7 | } 8 | 9 | // CHECK: handshake.func @sink(%{{.*}}: tuple, ...) 10 | // CHECK-NEXT: sink %{{.*}} : tuple 11 | // CHECK-NEXT: return 12 | // CHECK-NEXT: } 13 | } 14 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/projection/projection.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def increase(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([2, 4, 6])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([(1, 1), (2, 2), (3, 3)])) 15 | 16 | await out0Check 17 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/task-pipelining/task-pipelining.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([100,1,100])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([0,1,0])) 15 | 16 | await out0Check 17 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/combine/combine.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | in1 = Stream(ins[1]) 11 | out0 = Stream(outs[0]) 12 | 13 | cocotb.start_soon(in0.sendAndTerminate([1, 2, 3])) 14 | cocotb.start_soon(in1.sendAndTerminate([10, 11, 12])) 15 | 16 | await cocotb.start_soon(out0.checkOutputs([11, 13, 15])) 17 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/combine-with-reg/combine-with-reg.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | in1 = Stream(ins[1]) 11 | out0 = Stream(outs[0]) 12 | 13 | cocotb.start_soon(in0.sendAndTerminate([1, 2, 3])) 14 | cocotb.start_soon(in1.sendAndTerminate([10, 11, 12])) 15 | 16 | await cocotb.start_soon(out0.checkOutputs([11, 24, 39])) 17 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/split/split.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | 11 | out0 = Stream(outs[0]) 12 | out1 = Stream(outs[1]) 13 | 14 | out0Check = cocotb.start_soon(out0.checkOutputs([1,2,3])) 15 | out1Check = cocotb.start_soon(out1.checkOutputs([11,12,13])) 16 | 17 | cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 18 | 19 | await out0Check 20 | await out1Check 21 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/split-with-reg/split-with-reg.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | 11 | out0 = Stream(outs[0]) 12 | out1 = Stream(outs[1]) 13 | 14 | out0Check = cocotb.start_soon(out0.checkOutputs([1,2,3])) 15 | out1Check = cocotb.start_soon(out1.checkOutputs([1,3,5])) 16 | 17 | cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 18 | 19 | await out0Check 20 | await out1Check 21 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/Stream/StreamDialect.h: -------------------------------------------------------------------------------- 1 | //===- StreamDialect.h - Stream dialect -------------------------*- C++ -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_DIALECT_STREAM_H 10 | #define CIRCT_STREAM_DIALECT_STREAM_H 11 | 12 | #include "mlir/IR/Dialect.h" 13 | // Do not remove, otherwise includes will be reorder and this breaks everything 14 | #include "circt-stream/Dialect/Stream/StreamOpsDialect.h.inc" 15 | 16 | #endif // CIRCT_STREAM_DIALECT_STREAM_H 17 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | llvm_canonicalize_cmake_booleans( 2 | MLIR_ENABLE_BINDINGS_PYTHON 3 | ) 4 | 5 | configure_lit_site_cfg( 6 | ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in 7 | ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py 8 | MAIN_CONFIG 9 | ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py 10 | ) 11 | 12 | set(STREAM_TEST_DEPENDS 13 | FileCheck count not 14 | stream-opt 15 | ) 16 | 17 | add_lit_testsuite(check-stream "Running the stream regression tests" 18 | ${CMAKE_CURRENT_BINARY_DIR} 19 | DEPENDS ${STREAM_TEST_DEPENDS} 20 | ) 21 | set_target_properties(check-stream PROPERTIES FOLDER "Tests") 22 | 23 | add_lit_testsuites(STREAM ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${STREAM_TEST_DEPENDS}) 24 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/map/map.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([11,12,13])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 15 | 16 | await out0Check 17 | 18 | @cocotb.test() 19 | async def multipleEOS(dut): 20 | ins, outs = await initDut(dut) 21 | 22 | in0 = Stream(ins[0]) 23 | out0 = Stream(outs[0]) 24 | 25 | for _ in range(5): 26 | out0Check = cocotb.start_soon(out0.checkOutputs([11,12,13])) 27 | sending = cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 28 | 29 | await out0Check 30 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/filter/filter.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([1,2,4])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([0,1,2,0,4,0])) 15 | 16 | await out0Check 17 | 18 | @cocotb.test() 19 | async def multipleEOS(dut): 20 | ins, outs = await initDut(dut) 21 | 22 | in0 = Stream(ins[0]) 23 | out0 = Stream(outs[0]) 24 | 25 | for _ in range(5): 26 | out0Check = cocotb.start_soon(out0.checkOutputs([1,2,4])) 27 | 28 | cocotb.start_soon(in0.sendAndTerminate([0,1,2,0,4,0])) 29 | 30 | await out0Check 31 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/map-with-reg/map-with-reg.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([1,3,6])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 15 | 16 | await out0Check 17 | 18 | @cocotb.test() 19 | async def multipleEOS(dut): 20 | ins, outs = await initDut(dut) 21 | 22 | in0 = Stream(ins[0]) 23 | out0 = Stream(outs[0]) 24 | 25 | for _ in range(5): 26 | out0Check = cocotb.start_soon(out0.checkOutputs([1,3,6])) 27 | 28 | cocotb.start_soon(in0.sendAndTerminate([1,2,3])) 29 | 30 | await out0Check 31 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/filter-with-reg/filter-with-reg.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | 4 | 5 | @cocotb.test() 6 | async def ascendingInputs(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | 12 | out0Check = cocotb.start_soon(out0.checkOutputs([1,0,0])) 13 | 14 | cocotb.start_soon(in0.sendAndTerminate([0,1,2,0,4,0])) 15 | 16 | await out0Check 17 | 18 | @cocotb.test() 19 | async def multipleEOS(dut): 20 | ins, outs = await initDut(dut) 21 | 22 | in0 = Stream(ins[0]) 23 | out0 = Stream(outs[0]) 24 | 25 | for _ in range(5): 26 | out0Check = cocotb.start_soon(out0.checkOutputs([1,0,0])) 27 | 28 | cocotb.start_soon(in0.sendAndTerminate([0,1,2,0,4,0])) 29 | 30 | await out0Check 31 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/Stream/StreamTypes.h: -------------------------------------------------------------------------------- 1 | //===- StreamTypes.h - Stream Types -----------------------------*- C++ -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_DIALECT_STREAM_TYPES_H 10 | #define CIRCT_STREAM_DIALECT_STREAM_TYPES_H 11 | 12 | #include "mlir/IR/BuiltinTypes.h" 13 | #include "mlir/IR/TypeSupport.h" 14 | #include "mlir/IR/Types.h" 15 | 16 | #define GET_TYPEDEF_CLASSES 17 | #include "circt-stream/Dialect/Stream/StreamOpsTypes.h.inc" 18 | 19 | #endif // CIRCT_STREAM_DIALECT_STREAM_TYPES_H 20 | -------------------------------------------------------------------------------- /integration_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(STREAM_INTEGRATION_TEST_DEPENDS 2 | FileCheck count not split-file 3 | stream-opt) 4 | 5 | set(STREAM_INTEGRATION_TIMEOUT 60) # Set a 60s timeout on individual tests. 6 | configure_lit_site_cfg( 7 | ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in 8 | ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py 9 | INTEGRATION_CONFIG 10 | ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py 11 | ) 12 | 13 | add_lit_testsuite(check-stream-integration "Running the stream integration tests" 14 | ${CMAKE_CURRENT_BINARY_DIR} 15 | DEPENDS ${STREAM_INTEGRATION_TEST_DEPENDS} 16 | ) 17 | set_target_properties(check-stream-integration PROPERTIES FOLDER "IntegrationTests") 18 | 19 | add_lit_testsuites(STREAM_INTEGRATION ${CMAKE_CURRENT_SOURCE_DIR} 20 | DEPENDS ${STREAM_INTEGRATION_TEST_DEPENDS} 21 | ) 22 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/lit.local.cfg.py: -------------------------------------------------------------------------------- 1 | config.excludes.add('cocotb_bench.py') 2 | config.excludes.add('cocotb_driver.py') 3 | config.excludes.add('helper.py') 4 | config.excludes.add('combine.py') 5 | config.excludes.add('combine-with-reg.py') 6 | config.excludes.add('complex.py') 7 | config.excludes.add('filter.py') 8 | config.excludes.add('filter-with-reg.py') 9 | config.excludes.add('map.py') 10 | config.excludes.add('map-with-reg.py') 11 | config.excludes.add('projection.py') 12 | config.excludes.add('projection-with-reg.py') 13 | config.excludes.add('projection-selection.py') 14 | config.excludes.add('reduce.py') 15 | config.excludes.add('reduce-with-reg.py') 16 | config.excludes.add('split.py') 17 | config.excludes.add('split-with-reg.py') 18 | config.excludes.add('stream-stats.py') 19 | config.excludes.add('task-pipelining.py') 20 | -------------------------------------------------------------------------------- /include/circt-stream/Transform/CustomBufferInsertion.h: -------------------------------------------------------------------------------- 1 | //===- CustomBufferInsertion.h ----------------------------------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_TRANSFORM_CUSTOMBUFFERINSERTION_H_ 10 | #define CIRCT_STREAM_TRANSFORM_CUSTOMBUFFERINSERTION_H_ 11 | 12 | #include 13 | 14 | namespace mlir { 15 | class Pass; 16 | } 17 | 18 | namespace circt_stream { 19 | #define GEN_PASS_DECL_CUSTOMBUFFERINSERTION 20 | #include "circt-stream/Transform/Passes.h.inc" 21 | } // namespace circt_stream 22 | 23 | #endif // CIRCT_STREAM_TRANSFORM_CUSTOMBUFFERINSERTION_H_ 24 | -------------------------------------------------------------------------------- /include/circt-stream/Conversion/Passes.h: -------------------------------------------------------------------------------- 1 | //===- Passes.h - Conversion Pass Construction and Registration -----------===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_CONVERSION_PASSES_H 10 | #define CIRCT_STREAM_CONVERSION_PASSES_H 11 | 12 | #include "circt-stream/Conversion/StreamToHandshake.h" 13 | #include "mlir/Pass/PassRegistry.h" 14 | 15 | namespace circt_stream { 16 | /// Generate the code for registering conversion passes. 17 | #define GEN_PASS_REGISTRATION 18 | #include "circt-stream/Conversion/Passes.h.inc" 19 | 20 | } // namespace circt_stream 21 | 22 | #endif // CIRCT_STREAM_CONVERSION_PASSES_H 23 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/noop.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake --split-input-file | FileCheck %s 2 | 3 | func.func @noop(%in: !stream.stream) -> !stream.stream { 4 | return %in : !stream.stream 5 | } 6 | // CHECK-LABEL: handshake.func @noop( 7 | // CHECK-SAME: %[[VAL:.*]]: tuple, ...) -> tuple 8 | // CHECK-NEXT: return %[[VAL]] : tuple 9 | // CHECK-NEXT: } 10 | 11 | // ----- 12 | 13 | func.func @noop_multi_stream(%in0: !stream.stream, %in1: !stream.stream) -> (!stream.stream, !stream.stream) { 14 | return %in0, %in1 : !stream.stream, !stream.stream 15 | } 16 | 17 | // CHECK-LABEL: handshake.func @noop_multi_stream( 18 | // CHECK-SAME: %{{.*}}: tuple, 19 | // CHECK-SAME: %{{.*}}: tuple, ...) -> 20 | // CHECK-SAME: (tuple, tuple) 21 | // CHECK-NEXT: return %{{.*}}, %{{.*}} : tuple, tuple 22 | // CHECK-NEXT: } 23 | -------------------------------------------------------------------------------- /test/Dialect/Stream/reg-errors.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --split-input-file --verify-diagnostics 2 | 3 | func.func @wrong_reg_arg_type(%in: !stream.stream) -> !stream.stream { 4 | // expected-error @+1 {{expect the block argument #1 to have type 'i32', got 'i64' instead.}} 5 | %res = stream.map(%in) {registers = [0 : i32]}: (!stream.stream) -> !stream.stream { 6 | ^0(%val : i32, %reg: i64): 7 | %nReg = arith.addi %val, %val : i32 8 | stream.yield %nReg, %nReg : i32, i32 9 | } 10 | return %res : !stream.stream 11 | } 12 | 13 | // ----- 14 | 15 | func.func @wrong_reg_yield_type(%in: !stream.stream) -> !stream.stream { 16 | %res = stream.filter(%in) {registers = [0 : i32]}: (!stream.stream) -> !stream.stream { 17 | ^0(%val : i32, %reg: i32): 18 | %c0 = arith.constant 0 : i1 19 | // expected-error @+1 {{expect the operand #1 to have type 'i32', got 'i1' instead.}} 20 | stream.yield %c0, %c0 : i1, i1 21 | } 22 | return %res : !stream.stream 23 | } 24 | 25 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/Stream/StreamOps.h: -------------------------------------------------------------------------------- 1 | //===- StreamOps.h - Stream dialect ops -------------------------*- C++ -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_DIALECT_STREAM_OPS_H 10 | #define CIRCT_STREAM_DIALECT_STREAM_OPS_H 11 | 12 | #include "circt-stream/Dialect/Stream/StreamTypes.h" 13 | #include "mlir/IR/BuiltinTypes.h" 14 | #include "mlir/IR/Dialect.h" 15 | #include "mlir/IR/OpDefinition.h" 16 | #include "mlir/IR/PatternMatch.h" 17 | #include "mlir/Interfaces/ControlFlowInterfaces.h" 18 | #include "mlir/Interfaces/InferTypeOpInterface.h" 19 | #include "mlir/Interfaces/SideEffectInterfaces.h" 20 | 21 | #define GET_OP_CLASSES 22 | #include "circt-stream/Dialect/Stream/StreamOps.h.inc" 23 | 24 | #endif // CIRCT_STREAM_DIALECT_STREAM_OPS_H 25 | -------------------------------------------------------------------------------- /include/circt-stream/Conversion/StreamToHandshake.h: -------------------------------------------------------------------------------- 1 | //===- StreamToHandshake.h --------------------------------------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This file declares passes which together will lower the Stream dialect to 10 | // the Handshake dialect. 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | #ifndef CIRCT_STREAM_CONVERSION_STREAMTOHANDSHAKE_H_ 15 | #define CIRCT_STREAM_CONVERSION_STREAMTOHANDSHAKE_H_ 16 | 17 | #include 18 | 19 | namespace mlir { 20 | class Pass; 21 | } 22 | 23 | namespace circt_stream { 24 | #define GEN_PASS_DECL_STREAMTOHANDSHAKE 25 | #include "circt-stream/Conversion/Passes.h.inc" 26 | } // namespace circt_stream 27 | #endif // CIRCT_STREAM_CONVERSION_STREAMTOHANDSHAKE_H_ 28 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/projection/projection.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=projection --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // CHECK: ** TEST 11 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 12 | 13 | 14 | module { 15 | func.func @top(%in: !stream.stream>) -> !stream.stream { 16 | %out = stream.map(%in) : (!stream.stream>) -> !stream.stream { 17 | ^0(%val : tuple): 18 | %l, %r = stream.unpack %val : tuple 19 | %res = arith.addi %l, %r : i64 20 | stream.yield %res : i64 21 | } 22 | return %out : !stream.stream 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /include/circt-stream/Transform/Passes.h: -------------------------------------------------------------------------------- 1 | //===- Passes.h - Pass Entrypoints ------------------------------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This header file defines prototypes for CIRCT Stream transformation passes. 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef CIRCT_SREAM_TRANSFORMS_PASSES_H 14 | #define CIRCT_SREAM_TRANSFORMS_PASSES_H 15 | 16 | #include "circt-stream/Transform/CustomBufferInsertion.h" 17 | #include "mlir/Pass/Pass.h" 18 | 19 | namespace circt_stream { 20 | 21 | //===----------------------------------------------------------------------===// 22 | // Registration 23 | //===----------------------------------------------------------------------===// 24 | 25 | /// Generate the code for registering passes. 26 | #define GEN_PASS_REGISTRATION 27 | #include "circt-stream/Transform/Passes.h.inc" 28 | 29 | } // namespace circt_stream 30 | 31 | #endif // CIRCT_SREAM_TRANSFORMS_PASSES_H 32 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/Stream/StreamTypes.td: -------------------------------------------------------------------------------- 1 | //===- StreamTypes.td - Stream Types -----------------------*- tablegen -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_DIALECT_STREAM_TYPES_TD 10 | #define CIRCT_STREAM_DIALECT_STREAM_TYPES_TD 11 | 12 | include "mlir/IR/AttrTypeBase.td" 13 | include "circt-stream/Dialect/Stream/StreamDialect.td" 14 | 15 | class StreamDialect_Type traits = []> 16 | : TypeDef { 17 | let mnemonic = typeMnemonic; 18 | } 19 | 20 | def StreamType : StreamDialect_Type<"Stream", "stream"> { 21 | let summary = "A type for streams with elements of type elementType"; 22 | let description = [{ 23 | Parameterized stream type that is used to model streams with a fixed 24 | element type. 25 | }]; 26 | 27 | let parameters = (ins "::mlir::Type":$elementType); 28 | let assemblyFormat = "`<` $elementType `>`"; 29 | } 30 | 31 | #endif // CIRCT_STREAM_DIALECT_STREAM_TYPES_TD 32 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/multiple-ops.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | 3 | module { 4 | func.func @map(%in: !stream.stream) -> !stream.stream { 5 | %tmp = stream.map(%in) : (!stream.stream) -> !stream.stream { 6 | ^0(%val : i32): 7 | %0 = arith.constant 1 : i32 8 | %r = arith.addi %0, %val : i32 9 | stream.yield %r : i32 10 | } 11 | %res = stream.map(%tmp) : (!stream.stream) -> !stream.stream { 12 | ^0(%val : i32): 13 | %0 = arith.constant 10 : i32 14 | %r = arith.muli %0, %val : i32 15 | stream.yield %r : i32 16 | } 17 | return %res : !stream.stream 18 | } 19 | 20 | // CHECK: handshake.func private @[[LABEL_1:.*]](%{{.*}}: tuple, ...) -> tuple 21 | 22 | // CHECK: handshake.func private @[[LABEL_0:.*]](%{{.*}}: tuple, ...) -> tuple 23 | 24 | // CHECK: handshake.func @map(%{{.*}}: tuple, ...) -> tuple 25 | // CHECK-NEXT: %{{.*}} = instance @[[LABEL_0]](%{{.*}}) : (tuple) -> tuple 26 | // CHECK-NEXT: %{{.*}} = instance @[[LABEL_1]](%{{.*}}) : (tuple) -> tuple 27 | // CHECK-NEXT: return %{{.*}} : tuple 28 | // CHECK-NEXT: } 29 | } 30 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LIBS 2 | MLIRIR 3 | MLIRLLVMDialect 4 | MLIRMemRefDialect 5 | MLIROptLib 6 | MLIRParser 7 | MLIRFuncDialect 8 | MLIRSupport 9 | MLIRTransforms 10 | MLIRSCFDialect 11 | MLIRSCFToControlFlow 12 | 13 | CIRCTAffineToPipeline 14 | CIRCTAnalysisTestPasses 15 | CIRCTExportVerilog 16 | CIRCTFIRRTL 17 | CIRCTFIRRTLToHW 18 | CIRCTFIRRTLTransforms 19 | CIRCTHandshake 20 | CIRCTHandshakeToFIRRTL 21 | CIRCTHandshakeToHW 22 | CIRCTHandshakeTransforms 23 | CIRCTLLHD 24 | CIRCTLLHDToLLVM 25 | CIRCTLLHDTransforms 26 | CIRCTHW 27 | CIRCTHWToLLHD 28 | CIRCTHWTransforms 29 | CIRCTScheduling 30 | CIRCTSchedulingTestPasses 31 | CIRCTSeq 32 | CIRCTSeqTransforms 33 | CIRCTStandardToHandshake 34 | CIRCTPipelineOps 35 | CIRCTSV 36 | CIRCTSVTransforms 37 | CIRCTTransforms 38 | 39 | CIRCTStreamStream 40 | CIRCTStreamStreamToHandshake 41 | CIRCTStreamTransform 42 | ) 43 | add_llvm_executable(stream-opt stream-opt.cpp) 44 | 45 | llvm_update_compile_flags(stream-opt) 46 | target_link_libraries(stream-opt PRIVATE ${LIBS}) 47 | 48 | mlir_check_all_link_libraries(stream-opt) 49 | -------------------------------------------------------------------------------- /include/circt-stream/Conversion/Passes.td: -------------------------------------------------------------------------------- 1 | //===-- Passes.td - Conversion pass definitions ------------*- tablegen -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This file contains definitions for all dialect conversions. 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef CIRCT_STREAM_CONVERSION_PASSES_TD 14 | #define CIRCT_STREAM_CONVERSION_PASSES_TD 15 | 16 | include "mlir/Pass/PassBase.td" 17 | 18 | //===----------------------------------------------------------------------===// 19 | // StreamToHandshake 20 | //===----------------------------------------------------------------------===// 21 | 22 | def StreamToHandshake : Pass<"convert-stream-to-handshake", "mlir::ModuleOp"> { 23 | let summary = "Convert the Stream dialect to Handshake"; 24 | let dependentDialects = [ 25 | "circt_stream::stream::StreamDialect", 26 | "mlir::cf::ControlFlowDialect", 27 | "mlir::func::FuncDialect", 28 | "mlir::memref::MemRefDialect", 29 | "circt::handshake::HandshakeDialect" 30 | ]; 31 | } 32 | 33 | #endif // CIRCT_STREAM_CONVERSION_PASSES_TD 34 | 35 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/projection-with-reg/projection-with-reg.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=projection-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // CHECK: ** TEST 11 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 12 | 13 | 14 | module { 15 | func.func @top(%in: !stream.stream>) -> !stream.stream> { 16 | %out = stream.map(%in) {registers = [0 : i64] }: (!stream.stream>) -> !stream.stream> { 17 | ^0(%val : tuple, %reg: i64): 18 | %l, %r = stream.unpack %val : tuple 19 | %sum = arith.addi %l, %r : i64 20 | %c = arith.cmpi ult, %sum, %reg : i64 21 | %max = arith.select %c, %sum, %reg : i64 22 | %res = stream.pack %sum, %max : tuple 23 | stream.yield %res, %max : tuple, i64 24 | } 25 | return %out : !stream.stream> 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /include/circt-stream/Transform/Passes.td: -------------------------------------------------------------------------------- 1 | //===-- Passes.td - Transforms pass definition file --------*- tablegen -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This file contains definitions for passes within the Transforms/ directory. 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef CIRCT_STREAM_TRANSFORMS_PASSES 14 | #define CIRCT_STREAM_TRANSFORMS_PASSES 15 | 16 | include "mlir/Pass/PassBase.td" 17 | include "mlir/Rewrite/PassUtil.td" 18 | 19 | def CustomBufferInsertion : Pass<"custom-buffer-insertion", "circt::handshake::FuncOp"> { 20 | let summary = "Custom buffer insertion"; 21 | let description = [{ 22 | This pass inserts buffers everywhere, except in cycles. The goal of this pass is 23 | to increase the throughput, but reducing the latency of a cycle. 24 | }]; 25 | let dependentDialects = ["circt::handshake::HandshakeDialect"]; 26 | let options = [ 27 | Option<"fifoBufferSize", "fifobuffer-size", "unsigned", /*default=*/"10", 28 | "Number of slots in each fifo buffer">, 29 | ]; 30 | } 31 | 32 | #endif // CIRCT_STREAM_TRANSFORMS_PASSES 33 | 34 | -------------------------------------------------------------------------------- /lib/Dialect/Stream/StreamDialect.cpp: -------------------------------------------------------------------------------- 1 | //===- StreamDialect.cpp - Stream dialect --------------........-*- C++ -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #include "circt-stream/Dialect/Stream/StreamDialect.h" 10 | 11 | #include "circt-stream/Dialect/Stream/StreamOps.h" 12 | #include "circt-stream/Dialect/Stream/StreamTypes.h" 13 | #include "mlir/IR/DialectImplementation.h" 14 | #include "llvm/ADT/TypeSwitch.h" 15 | 16 | using namespace mlir; 17 | using namespace circt_stream; 18 | using namespace circt_stream::stream; 19 | 20 | #include "circt-stream/Dialect/Stream/StreamOpsDialect.cpp.inc" 21 | 22 | //===----------------------------------------------------------------------===// 23 | // Stream dialect. 24 | //===----------------------------------------------------------------------===// 25 | 26 | void StreamDialect::initialize() { 27 | addOperations< 28 | #define GET_OP_LIST 29 | #include "circt-stream/Dialect/Stream/StreamOps.cpp.inc" 30 | >(); 31 | addTypes< 32 | #define GET_TYPEDEF_LIST 33 | #include "circt-stream/Dialect/Stream/StreamOpsTypes.cpp.inc" 34 | >(); 35 | } 36 | 37 | #define GET_TYPEDEF_CLASSES 38 | #include "circt-stream/Dialect/Stream/StreamOpsTypes.cpp.inc" 39 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/projection-selection/projection-selection.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=projection-selection --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // CHECK: ** TEST 11 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 12 | 13 | 14 | !T = tuple 15 | !Tout = tuple 16 | 17 | module { 18 | func.func @top(%in: !stream.stream) -> !stream.stream { 19 | %mapOut = stream.map(%in) : (!stream.stream) -> !stream.stream { 20 | ^0(%val : !T): 21 | %e:8 = stream.unpack %val : !T 22 | %f0 = arith.addi %e#0, %e#1 : i64 23 | %f1 = arith.addi %e#4, %e#5 : i64 24 | %res = stream.pack %f0, %f1 : !Tout 25 | stream.yield %res : !Tout 26 | } 27 | %out = stream.filter(%mapOut) : (!stream.stream) -> !stream.stream { 28 | ^0(%val : !Tout): 29 | %e:2 = stream.unpack %val : !Tout 30 | %cond = arith.cmpi sle, %e#0, %e#1 : i64 31 | stream.yield %cond : i1 32 | } 33 | return %out : !stream.stream 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/projection-with-reg/projection-with-reg.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | import random 4 | 5 | 6 | async def initStreams(dut): 7 | ins, outs = await initDut(dut) 8 | 9 | in0 = Stream(ins[0]) 10 | out0 = Stream(outs[0]) 11 | return in0, out0 12 | 13 | @cocotb.test() 14 | async def increase(dut): 15 | inStream, outStream = await initStreams(dut) 16 | 17 | resCheck = cocotb.start_soon(outStream.checkOutputs([(2, 2), (4, 4), 18 | (6, 6)])) 19 | 20 | cocotb.start_soon(inStream.sendAndTerminate([(1, 1), (2, 2), (3, 3)])) 21 | 22 | await resCheck 23 | 24 | 25 | @cocotb.test() 26 | async def mixed(dut): 27 | inStream, outStream = await initStreams(dut) 28 | 29 | resCheck = cocotb.start_soon( 30 | outStream.checkOutputs([(11, 11), (4, 11), (30, 30), (29, 30)])) 31 | 32 | inputs = [(1, 10), (2, 2), (30, 0), (15, 14)] 33 | cocotb.start_soon(inStream.sendAndTerminate(inputs)) 34 | 35 | await resCheck 36 | 37 | 38 | @cocotb.test() 39 | async def randomized(dut): 40 | inStream, outStream = await initStreams(dut) 41 | 42 | N = 100 43 | 44 | inputs = [(random.randint(0, 1000), random.randint(0, 1000)) 45 | for _ in range(N)] 46 | outputs = [] 47 | m = 0 48 | for (l, r) in inputs: 49 | s = l + r 50 | m = max(s, m) 51 | outputs.append((s, m)) 52 | 53 | resCheck = cocotb.start_soon(outStream.checkOutputs(outputs)) 54 | cocotb.start_soon(inStream.sendAndTerminate(inputs)) 55 | 56 | await resCheck 57 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/projection-selection/projection-selection.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | import random 4 | 5 | 6 | @cocotb.test() 7 | async def single(dut): 8 | ins, outs = await initDut(dut) 9 | 10 | in0 = Stream(ins[0]) 11 | out0 = Stream(outs[0]) 12 | 13 | out0Check = cocotb.start_soon(out0.checkOutputs([(3, 11)])) 14 | 15 | cocotb.start_soon(in0.sendAndTerminate([(1, 2, 3, 4, 5, 6, 7, 8)])) 16 | 17 | await out0Check 18 | 19 | 20 | def randomTuple(): 21 | return tuple([random.randint(0, 100) for _ in range(8)]) 22 | 23 | def getOutpus(inputs): 24 | asLists = [list(i) for i in inputs] 25 | return [(l,r) for i in asLists if (l := i[0] + i[1]) <= (r := i[4] + i[5])] 26 | 27 | 28 | @cocotb.test() 29 | async def multiple(dut): 30 | ins, outs = await initDut(dut) 31 | 32 | in0 = Stream(ins[0]) 33 | out0 = Stream(outs[0]) 34 | 35 | N = 100 36 | inputs = [randomTuple() for _ in range(N)] 37 | outputs = getOutpus(inputs) 38 | 39 | out0Check = cocotb.start_soon(out0.checkOutputs(outputs)) 40 | cocotb.start_soon(in0.sendAndTerminate(inputs)) 41 | 42 | await out0Check 43 | 44 | @cocotb.test() 45 | async def multipleEOS(dut): 46 | ins, outs = await initDut(dut) 47 | 48 | in0 = Stream(ins[0]) 49 | out0 = Stream(outs[0]) 50 | 51 | for _ in range(3): 52 | N = 10 53 | inputs = [randomTuple() for _ in range(N)] 54 | outputs = getOutpus(inputs) 55 | 56 | out0Check = cocotb.start_soon(out0.checkOutputs(outputs)) 57 | cocotb.start_soon(in0.sendAndTerminate(inputs)) 58 | 59 | await out0Check 60 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/Stream/StreamDialect.td: -------------------------------------------------------------------------------- 1 | //===- StreamDialect.td - Stream dialect -------------------*- tablegen -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_DIALECT_STREAM_TD 10 | #define CIRCT_STREAM_DIALECT_STREAM_TD 11 | 12 | include "mlir/IR/OpBase.td" 13 | 14 | //===----------------------------------------------------------------------===// 15 | // Stream dialect definition. 16 | //===----------------------------------------------------------------------===// 17 | 18 | def Stream_Dialect : Dialect { 19 | let name = "stream"; 20 | let summary = "A dialect for stream processing."; 21 | let description = [{ 22 | The stream dialect provides a streaming abstraction that was designed 23 | to be lowered to hardware. It includes operations to work with streams 24 | and helpers that are used to process stream elements. 25 | }]; 26 | 27 | let useDefaultTypePrinterParser = 1; 28 | //let useDefaultAttributePrinterParser = 1; 29 | 30 | let cppNamespace = "::circt_stream::stream"; 31 | } 32 | 33 | //===----------------------------------------------------------------------===// 34 | // Base stream operation definition. 35 | //===----------------------------------------------------------------------===// 36 | 37 | class Stream_Op traits = []> : 38 | Op; 39 | 40 | #endif // CIRCT_STREAM_DIALECT_STREAM_TD 41 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/stream-stats/stream-stats.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | from helper import initDut, Stream 3 | import random 4 | 5 | 6 | @cocotb.test() 7 | async def sendOne(dut): 8 | ins, outs = await initDut(dut) 9 | 10 | in0 = Stream(ins[0]) 11 | out0 = Stream(outs[0]) 12 | out1 = Stream(outs[1]) 13 | 14 | inputs = [(10, 2, 3, 6, 5, 1, 5, 9)] 15 | outputs = [max([max(list(i)) for i in inputs])] 16 | 17 | out0Check = cocotb.start_soon(out0.checkOutputs(inputs)) 18 | out1Check = cocotb.start_soon(out1.checkOutputs(outputs)) 19 | 20 | cocotb.start_soon(in0.sendAndTerminate(inputs)) 21 | 22 | await out0Check 23 | await out1Check 24 | 25 | 26 | def randomTuple(): 27 | return tuple([random.randint(0, 100) for _ in range(8)]) 28 | 29 | 30 | @cocotb.test() 31 | async def sendMultiple(dut): 32 | ins, outs = await initDut(dut) 33 | 34 | in0 = Stream(ins[0]) 35 | out0 = Stream(outs[0]) 36 | out1 = Stream(outs[1]) 37 | 38 | N = 100 39 | inputs = [randomTuple() for _ in range(N)] 40 | outputs = [max([max(list(i)) for i in inputs])] 41 | 42 | out0Check = cocotb.start_soon(out0.checkOutputs(inputs)) 43 | out1Check = cocotb.start_soon(out1.checkOutputs(outputs)) 44 | 45 | cocotb.start_soon(in0.sendAndTerminate(inputs)) 46 | 47 | await out0Check 48 | await out1Check 49 | 50 | 51 | @cocotb.test() 52 | async def sendMultipleWithEOS(dut): 53 | ins, outs = await initDut(dut) 54 | 55 | in0 = Stream(ins[0]) 56 | out0 = Stream(outs[0]) 57 | out1 = Stream(outs[1]) 58 | 59 | for _ in range(5): 60 | N = 100 61 | inputs = [randomTuple() for _ in range(N)] 62 | outputs = [max([max(list(i)) for i in inputs])] 63 | 64 | out0Check = cocotb.start_soon(out0.checkOutputs(inputs)) 65 | out1Check = cocotb.start_soon(out1.checkOutputs(outputs)) 66 | 67 | cocotb.start_soon(in0.sendAndTerminate(inputs)) 68 | 69 | await out0Check 70 | await out1Check 71 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/map/map.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=map --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=map --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=map --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %out = stream.map(%in) : (!stream.stream) -> !stream.stream { 30 | ^0(%val : i64): 31 | %0 = arith.constant 10 : i64 32 | %r = arith.addi %0, %val : i64 33 | stream.yield %r : i64 34 | } 35 | return %out : !stream.stream 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/reduce/reduce.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=reduce --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=reduce --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=reduce --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %out = stream.reduce(%in) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 30 | ^0(%acc: i64, %val: i64): 31 | %r = arith.addi %acc, %val : i64 32 | stream.yield %r : i64 33 | } 34 | return %out : !stream.stream 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/filter/filter.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=filter --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=filter --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=filter --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %out = stream.filter(%in) : (!stream.stream) -> !stream.stream { 30 | ^bb0(%val: i64): 31 | %c0_i64 = arith.constant 0 : i64 32 | %0 = arith.cmpi sgt, %val, %c0_i64 : i64 33 | stream.yield %0 : i1 34 | } 35 | return %out : !stream.stream 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/combine/combine.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=combine --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=combine --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=combine --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in0: !stream.stream, %in1: !stream.stream) -> (!stream.stream) { 29 | %res = stream.combine(%in0, %in1) : (!stream.stream, !stream.stream) -> (!stream.stream) { 30 | ^0(%val0: i64, %val1: i64): 31 | %0 = arith.addi %val0, %val1 : i64 32 | stream.yield %0 : i64 33 | } 34 | return %res : !stream.stream 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/map-with-reg/map-with-reg.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=map-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=map-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=map-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %res = stream.map(%in) {registers = [0: i64]}: (!stream.stream) -> !stream.stream { 30 | ^0(%val : i64, %reg: i64): 31 | %nReg = arith.addi %val, %reg : i64 32 | stream.yield %nReg, %nReg : i64, i64 33 | } 34 | return %res : !stream.stream 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/filter-with-reg/filter-with-reg.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=filter-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=filter-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=filter-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %out = stream.filter(%in) {registers = [0 : i1]}: (!stream.stream) -> !stream.stream { 30 | ^bb0(%val: i64, %reg: i1): 31 | %c1 = arith.constant 1 : i1 32 | %nReg = arith.xori %c1, %reg : i1 33 | stream.yield %reg, %nReg : i1, i1 34 | } 35 | return %out : !stream.stream 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/split/split.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=split --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=split --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=split --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> (!stream.stream, !stream.stream) { 29 | %res0, %res1 = stream.split(%in) : (!stream.stream) -> (!stream.stream, !stream.stream) { 30 | ^0(%val: i64): 31 | %c10 = arith.constant 10 : i64 32 | %snd = arith.addi %val, %c10 : i64 33 | stream.yield %val, %snd : i64, i64 34 | } 35 | return %res0, %res1 : !stream.stream, !stream.stream 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/filter.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | 3 | func.func @filter(%in: !stream.stream) -> !stream.stream { 4 | %out = stream.filter(%in) : (!stream.stream) -> !stream.stream { 5 | ^bb0(%val: i32): 6 | %c0_i32 = arith.constant 0 : i32 7 | %0 = arith.cmpi sgt, %val, %c0_i32 : i32 8 | stream.yield %0 : i1 9 | } 10 | return %out : !stream.stream 11 | } 12 | 13 | // CHECK-LABEL: handshake.func private @stream_filter( 14 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 15 | // CHECK: %[[VAL_1:.*]]:2 = fork [2] %[[VAL_0]] : tuple 16 | // CHECK: %[[VAL_2:.*]]:2 = unpack %[[VAL_1]]#1 : tuple 17 | // CHECK: %[[VAL_3:.*]]:4 = fork [4] %[[VAL_2]]#1 : i1 18 | // CHECK: %[[VAL_4:.*]], %[[VAL_5:.*]] = cond_br %[[VAL_3]]#3, %[[VAL_2]]#0 : i32 19 | // CHECK: sink %[[VAL_4]] : i32 20 | // CHECK: %[[VAL_6:.*]]:2 = fork [2] %[[VAL_5]] : i32 21 | // CHECK: %[[VAL_7:.*]] = join %[[VAL_6]]#1 : i32 22 | // CHECK: %[[VAL_8:.*]] = merge %[[VAL_6]]#0 : i32 23 | // CHECK: %[[VAL_9:.*]] = constant %[[VAL_7]] {value = 0 : i32} : i32 24 | // CHECK: %[[VAL_10:.*]] = arith.cmpi sgt, %[[VAL_8]], %[[VAL_9]] : i32 25 | // CHECK: %[[VAL_11:.*]], %[[VAL_12:.*]] = cond_br %[[VAL_3]]#1, %[[VAL_3]]#2 : i1 26 | // CHECK: sink %[[VAL_12]] : i1 27 | // CHECK: %[[VAL_13:.*]] = mux %[[VAL_3]]#0 {{\[}}%[[VAL_10]], %[[VAL_11]]] : i1, i1 28 | // CHECK: %[[VAL_14:.*]], %[[VAL_15:.*]] = cond_br %[[VAL_13]], %[[VAL_1]]#0 : tuple 29 | // CHECK: sink %[[VAL_15]] : tuple 30 | // CHECK: return %[[VAL_14]] : tuple 31 | // CHECK: } 32 | 33 | // CHECK-LABEL: handshake.func @filter( 34 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 35 | // CHECK: %[[VAL_1:.*]] = instance @stream_filter(%[[VAL_0]]) : (tuple) -> tuple 36 | // CHECK: return %[[VAL_1]] : tuple 37 | // CHECK: } 38 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/reduce-with-reg/reduce-with-reg.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=reduce-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=reduce-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=reduce-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %out = stream.reduce(%in) {initValue = 0 : i64, registers = [10 : i64]}: (!stream.stream) -> !stream.stream { 30 | ^0(%acc: i64, %val: i64, %reg: i64): 31 | %r = arith.addi %acc, %val : i64 32 | %res = arith.addi %r, %reg : i64 33 | stream.yield %res, %reg : i64, i64 34 | } 35 | return %out : !stream.stream 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/split-with-reg/split-with-reg.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=split-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=split-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=split-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> (!stream.stream, !stream.stream) { 29 | %res0, %res1 = stream.split(%in) {registers = [0 : i64]}: (!stream.stream) -> (!stream.stream, !stream.stream) { 30 | ^0(%val: i64, %reg: i64): 31 | %snd = arith.addi %val, %reg : i64 32 | stream.yield %val, %snd, %val : i64, i64, i64 33 | } 34 | return %res0, %res1 : !stream.stream, !stream.stream 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/combine-with-reg/combine-with-reg.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=combine-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=combine-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=combine-with-reg --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in0: !stream.stream, %in1: !stream.stream) -> (!stream.stream) { 29 | %res = stream.combine(%in0, %in1) {registers = [0 : i64]} : (!stream.stream, !stream.stream) -> (!stream.stream) { 30 | ^0(%val0: i64, %val1: i64, %reg: i64): 31 | %0 = arith.addi %val0, %val1 : i64 32 | %nReg = arith.addi %0, %reg : i64 33 | stream.yield %nReg, %nReg : i64, i64 34 | } 35 | return %res : !stream.stream 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/lit.site.cfg.py.in: -------------------------------------------------------------------------------- 1 | @LIT_SITE_CFG_IN_HEADER@ 2 | 3 | import sys 4 | 5 | config.host_triple = "@LLVM_HOST_TRIPLE@" 6 | config.target_triple = "@TARGET_TRIPLE@" 7 | config.llvm_src_root = "@LLVM_SOURCE_DIR@" 8 | config.llvm_obj_root = "@LLVM_BINARY_DIR@" 9 | config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" 10 | config.llvm_lib_dir = "@LLVM_LIBS_DIR@" 11 | config.llvm_shlib_dir = "@SHLIBDIR@" 12 | config.llvm_shlib_ext = "@SHLIBEXT@" 13 | config.llvm_exe_ext = "@EXEEXT@" 14 | config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" 15 | config.mlir_binary_dir = "@MLIR_BINARY_DIR@" 16 | config.python_executable = "@Python3_EXECUTABLE@" 17 | config.enable_bindings_python = @MLIR_ENABLE_BINDINGS_PYTHON@ 18 | config.gold_executable = "@GOLD_EXECUTABLE@" 19 | config.ld64_executable = "@LD64_EXECUTABLE@" 20 | config.enable_shared = @ENABLE_SHARED@ 21 | config.enable_assertions = @ENABLE_ASSERTIONS@ 22 | config.targets_to_build = "@TARGETS_TO_BUILD@" 23 | config.native_target = "@LLVM_NATIVE_ARCH@" 24 | config.llvm_bindings = "@LLVM_BINDINGS@".split(' ') 25 | config.host_os = "@HOST_OS@" 26 | config.host_cc = "@HOST_CC@" 27 | config.host_cxx = "@HOST_CXX@" 28 | config.enable_libcxx = "@LLVM_ENABLE_LIBCXX@" 29 | # Note: ldflags can contain double-quoted paths, so must use single quotes here. 30 | config.host_ldflags = '@HOST_LDFLAGS@' 31 | config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@" 32 | config.llvm_host_triple = '@LLVM_HOST_TRIPLE@' 33 | config.host_arch = "@HOST_ARCH@" 34 | config.stream_src_root = "@CMAKE_SOURCE_DIR@" 35 | config.stream_obj_root = "@CMAKE_BINARY_DIR@" 36 | 37 | # Support substitution of the tools_dir with user parameters. This is 38 | # used when we can't determine the tool dir at configuration time. 39 | try: 40 | config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params 41 | config.llvm_lib_dir = config.llvm_lib_dir % lit_config.params 42 | config.llvm_shlib_dir = config.llvm_shlib_dir % lit_config.params 43 | except KeyError: 44 | e = sys.exc_info()[1] 45 | key, = e.args 46 | lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) 47 | 48 | 49 | import lit.llvm 50 | lit.llvm.initialize(lit_config, config) 51 | 52 | # Let the main config do the real work. 53 | lit_config.load_config(config, "@CMAKE_SOURCE_DIR@/test/lit.cfg.py") 54 | -------------------------------------------------------------------------------- /test/lit.cfg.py: -------------------------------------------------------------------------------- 1 | # -*- Python -*- 2 | 3 | import os 4 | import platform 5 | import re 6 | import subprocess 7 | import tempfile 8 | 9 | import lit.formats 10 | import lit.util 11 | 12 | from lit.llvm import llvm_config 13 | from lit.llvm.subst import ToolSubst 14 | from lit.llvm.subst import FindTool 15 | 16 | # Configuration file for the 'lit' test runner. 17 | 18 | # name: The name of this test suite. 19 | config.name = 'Stream' 20 | 21 | config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) 22 | 23 | # suffixes: A list of file extensions to treat as test files. 24 | config.suffixes = ['.mlir'] 25 | 26 | # test_source_root: The root path where tests are located. 27 | config.test_source_root = os.path.dirname(__file__) 28 | 29 | # test_exec_root: The root path where tests should be run. 30 | config.test_exec_root = os.path.join(config.stream_obj_root, 'test') 31 | 32 | config.substitutions.append(('%PATH%', config.environment['PATH'])) 33 | config.substitutions.append(('%shlibext', config.llvm_shlib_ext)) 34 | 35 | llvm_config.with_system_environment( 36 | ['HOME', 'INCLUDE', 'LIB', 'TMP', 'TEMP']) 37 | 38 | llvm_config.use_default_substitutions() 39 | 40 | # excludes: A list of directories to exclude from the testsuite. The 'Inputs' 41 | # subdirectories contain auxiliary inputs for various tests in their parent 42 | # directories. 43 | config.excludes = ['Inputs', 'Examples', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt'] 44 | 45 | # test_source_root: The root path where tests are located. 46 | config.test_source_root = os.path.dirname(__file__) 47 | 48 | # test_exec_root: The root path where tests should be run. 49 | config.test_exec_root = os.path.join(config.stream_obj_root, 'test') 50 | config.stream_tools_dir = os.path.join(config.stream_obj_root, 'bin') 51 | 52 | # Tweak the PATH to include the tools dir. 53 | llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True) 54 | 55 | tool_dirs = [config.stream_tools_dir, config.llvm_tools_dir] 56 | tools = [ 57 | 'stream-opt', 58 | ToolSubst('%PYTHON', config.python_executable, unresolved='ignore'), 59 | ] 60 | 61 | llvm_config.add_tool_substitutions(tools, tool_dirs) 62 | 63 | llvm_config.with_environment('PYTHONPATH', [ 64 | os.path.join(config.mlir_binary_dir, 'python_packages', 'stream'), 65 | ], append_path=True) 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CIRCT Stream 2 | 3 | This project aims to add a streaming abstraction on top of [CIRCT](https://circt.llvm.org/) which in itself depends on [MLIR](https://mlir.llvm.org/). 4 | 5 | ## Setup 6 | 7 | ### Prerequisites 8 | 9 | Apart from `cmake` and `Ninja`, this project does not come with any prerequisites. 10 | To reduce the amount of memory required, it is beneficial to install llvm's `lld`, as it is much more memory efficent than `ld`. 11 | 12 | ### Cloning 13 | 14 | This project has a pinned version of CIRCT which in turn comes with a pinned verison of LLVM. 15 | These submodules can directly be initialized when the repository is cloned: 16 | 17 | ```sh 18 | git clone --recurse-submodules git@github.com:Dinistro/circt-stream.git 19 | ``` 20 | 21 | ### Building 22 | 23 | Before the `circt-stream` project can be built, its dependencies need to be built. 24 | 25 | To make things easier, one should build CIRCT together with LLVM as follows: 26 | 27 | ```sh 28 | mkdir circt/build && cd circt/build 29 | cmake -G Ninja ../llvm/llvm \ 30 | -DCMAKE_BUILD_TYPE=Debug \ 31 | -DLLVM_ENABLE_PROJECTS=mlir \ 32 | -DLLVM_ENABLE_ASSERTIONS=ON \ 33 | -DLLVM_EXTERNAL_PROJECTS=circt \ 34 | -DLLVM_EXTERNAL_CIRCT_SOURCE_DIR=.. \ 35 | -DLLVM_USE_LINKER=lld \ # remove if lld isn't present 36 | -DCMAKE_C_COMPILER=clang \ 37 | -DCMAKE_CXX_COMPILER=clang++ \ 38 | -DLLVM_CCACHE_BUILD=1 # remove if CCache isn't present 39 | ninja 40 | ``` 41 | 42 | After CIRCT has been built, one can build this project as follows: 43 | 44 | ```sh 45 | mkdir build && cd build 46 | cmake -G Ninja .. \ 47 | -DLLVM_DIR=$PWD/../circt/build/lib/cmake/llvm \ 48 | -DMLIR_DIR=$PWD/../circt/build/lib/cmake/mlir \ 49 | -DCIRCT_DIR=$PWD/../circt/build/lib/cmake/circt \ 50 | -DLLVM_EXTERNAL_LIT=$PWD/../circt/build/bin/llvm-lit \ 51 | -DLLVM_ENABLE_ASSERTIONS=ON \ 52 | -DCMAKE_BUILD_TYPE=DEBUG \ 53 | -DLLVM_USE_LINKER=lld \ # remove if lld isn't present 54 | -DCMAKE_C_COMPILER=clang \ 55 | -DCMAKE_CXX_COMPILER=clang++ 56 | ninja 57 | ``` 58 | 59 | ### Testing 60 | 61 | There are two types of tests. FileCheck tests can be executed with the following command: 62 | 63 | ```sh 64 | ninja check-stream 65 | ``` 66 | 67 | To execute the integration tests, an installation of Icarus Verilog and pythons `cocotb` and `cocotb-test` packages are required. 68 | 69 | ```sh 70 | ninja check-stream-integration 71 | ``` 72 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/combine.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | 3 | func.func @combine(%in0: !stream.stream, %in1: !stream.stream) -> (!stream.stream) { 4 | %res = stream.combine(%in0, %in1) : (!stream.stream, !stream.stream) -> (!stream.stream) { 5 | ^0(%val0: i32, %val1: i32): 6 | %0 = arith.addi %val0, %val1 : i32 7 | stream.yield %0 : i32 8 | } 9 | return %res : !stream.stream 10 | } 11 | 12 | // CHECK-LABEL: handshake.func private @stream_combine( 13 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, %[[VAL_1:.*]]: tuple, ...) -> tuple 14 | // CHECK: %[[VAL_2:.*]]:2 = unpack %[[VAL_0]] : tuple 15 | // CHECK: %[[VAL_3:.*]]:2 = unpack %[[VAL_1]] : tuple 16 | // CHECK: %[[VAL_4:.*]] = arith.ori %[[VAL_2]]#1, %[[VAL_3]]#1 : i1 17 | // CHECK: %[[VAL_5:.*]]:4 = fork [4] %[[VAL_4]] : i1 18 | // CHECK: %[[VAL_6:.*]], %[[VAL_7:.*]] = cond_br %[[VAL_5]]#3, %[[VAL_2]]#0 : i32 19 | // CHECK: sink %[[VAL_6]] : i32 20 | // CHECK: %[[VAL_8:.*]]:2 = fork [2] %[[VAL_7]] : i32 21 | // CHECK: %[[VAL_9:.*]], %[[VAL_10:.*]] = cond_br %[[VAL_5]]#2, %[[VAL_3]]#0 : i32 22 | // CHECK: sink %[[VAL_9]] : i32 23 | // CHECK: %[[VAL_11:.*]]:2 = fork [2] %[[VAL_10]] : i32 24 | // CHECK: %[[VAL_12:.*]] = join %[[VAL_8]]#1, %[[VAL_11]]#1 : i32, i32 25 | // CHECK: sink %[[VAL_12]] : none 26 | // CHECK: %[[VAL_13:.*]] = merge %[[VAL_8]]#0 : i32 27 | // CHECK: %[[VAL_14:.*]] = merge %[[VAL_11]]#0 : i32 28 | // CHECK: %[[VAL_15:.*]] = arith.addi %[[VAL_13]], %[[VAL_14]] : i32 29 | // CHECK: %[[VAL_16:.*]] = source 30 | // CHECK: %[[VAL_17:.*]] = constant %[[VAL_16]] {value = 0 : i32} : i32 31 | // CHECK: %[[VAL_18:.*]] = mux %[[VAL_5]]#1 {{\[}}%[[VAL_15]], %[[VAL_17]]] : i1, i32 32 | // CHECK: %[[VAL_19:.*]] = pack %[[VAL_18]], %[[VAL_5]]#0 : tuple 33 | // CHECK: return %[[VAL_19]] : tuple 34 | // CHECK: } 35 | 36 | // CHECK-LABEL: handshake.func @combine( 37 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, %[[VAL_1:.*]]: tuple, ...) -> tuple 38 | // CHECK: %[[VAL_2:.*]] = instance @stream_combine(%[[VAL_0]], %[[VAL_1]]) : (tuple, tuple) -> tuple 39 | // CHECK: return %[[VAL_2]] : tuple 40 | // CHECK: } 41 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/task-pipelining/task-pipelining.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=task-pipelining --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=task-pipelining --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=task-pipelining --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %out = stream.map(%in) : (!stream.stream) -> (!stream.stream) { 30 | ^0(%val: i64): 31 | %c0 = arith.constant 0 : i64 32 | %cond = arith.cmpi eq, %c0, %val : i64 33 | cf.cond_br %cond, ^1(%c0: i64), ^2(%val: i64) 34 | ^1(%i: i64): 35 | %c10 = arith.constant 100 : i64 36 | %lcond = arith.cmpi eq, %c10, %i : i64 37 | cf.cond_br %lcond, ^2(%i: i64), ^body 38 | ^body: 39 | %c1 = arith.constant 1 : i64 40 | %ni = arith.addi %i, %c1 : i64 41 | cf.br ^1(%ni: i64) 42 | ^2(%res: i64): 43 | stream.yield %res : i64 44 | } 45 | 46 | return %out : !stream.stream 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/pack_unpack.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | 3 | func.func @map(%in: !stream.stream>) -> !stream.stream> { 4 | %res = stream.map(%in) : (!stream.stream>) -> !stream.stream> { 5 | ^0(%val : tuple): 6 | %a, %b = stream.unpack %val : tuple 7 | %r = stream.pack %b, %a : tuple 8 | stream.yield %r : tuple 9 | } 10 | return %res : !stream.stream> 11 | } 12 | 13 | // CHECK-LABEL: handshake.func private @stream_map( 14 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, i1>, ...) -> tuple, i1> 15 | // CHECK: %[[VAL_1:.*]] = source 16 | // CHECK: %[[VAL_2:.*]]:2 = fork [2] %[[VAL_1]] : none 17 | // CHECK: %[[VAL_3:.*]] = constant %[[VAL_2]]#1 {value = 0 : i64} : i64 18 | // CHECK: %[[VAL_4:.*]] = constant %[[VAL_2]]#0 {value = 0 : i32} : i32 19 | // CHECK: %[[VAL_5:.*]] = pack %[[VAL_3]], %[[VAL_4]] : tuple 20 | // CHECK: %[[VAL_6:.*]] = mux %[[VAL_7:.*]]#1 {{\[}}%[[VAL_8:.*]], %[[VAL_5]]] : i1, tuple 21 | // CHECK: %[[VAL_9:.*]]:2 = unpack %[[VAL_0]] : tuple, i1> 22 | // CHECK: %[[VAL_7]]:3 = fork [3] %[[VAL_9]]#1 : i1 23 | // CHECK: %[[VAL_10:.*]], %[[VAL_11:.*]] = cond_br %[[VAL_7]]#2, %[[VAL_9]]#0 : tuple 24 | // CHECK: sink %[[VAL_10]] : tuple 25 | // CHECK: %[[VAL_12:.*]]:2 = fork [2] %[[VAL_11]] : tuple 26 | // CHECK: %[[VAL_13:.*]] = join %[[VAL_12]]#1 : tuple 27 | // CHECK: sink %[[VAL_13]] : none 28 | // CHECK: %[[VAL_14:.*]] = merge %[[VAL_12]]#0 : tuple 29 | // CHECK: %[[VAL_15:.*]]:2 = unpack %[[VAL_14]] : tuple 30 | // CHECK: %[[VAL_8]] = pack %[[VAL_15]]#1, %[[VAL_15]]#0 : tuple 31 | // CHECK: %[[VAL_16:.*]] = pack %[[VAL_6]], %[[VAL_7]]#0 : tuple, i1> 32 | // CHECK: return %[[VAL_16]] : tuple, i1> 33 | // CHECK: } 34 | 35 | // CHECK-LABEL: handshake.func @map( 36 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, i1>, ...) -> tuple, i1> 37 | // CHECK: %[[VAL_1:.*]] = instance @stream_map(%[[VAL_0]]) : (tuple, i1>) -> tuple, i1> 38 | // CHECK: return %[[VAL_1]] : tuple, i1> 39 | // CHECK: } 40 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/split.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | 3 | func.func @split(%in: !stream.stream>) -> (!stream.stream, !stream.stream) { 4 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 5 | ^0(%val: tuple): 6 | %0, %1 = stream.unpack %val : tuple 7 | stream.yield %0, %1 : i32, i32 8 | } 9 | return %res0, %res1 : !stream.stream, !stream.stream 10 | } 11 | 12 | // CHECK-LABEL: handshake.func private @stream_split( 13 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, i1>, ...) -> (tuple, tuple) 14 | // CHECK: %[[VAL_1:.*]]:2 = unpack %[[VAL_0]] : tuple, i1> 15 | // CHECK: %[[VAL_2:.*]]:5 = fork [5] %[[VAL_1]]#1 : i1 16 | // CHECK: %[[VAL_3:.*]], %[[VAL_4:.*]] = cond_br %[[VAL_2]]#4, %[[VAL_1]]#0 : tuple 17 | // CHECK: sink %[[VAL_3]] : tuple 18 | // CHECK: %[[VAL_5:.*]]:2 = fork [2] %[[VAL_4]] : tuple 19 | // CHECK: %[[VAL_6:.*]] = join %[[VAL_5]]#1 : tuple 20 | // CHECK: sink %[[VAL_6]] : none 21 | // CHECK: %[[VAL_7:.*]] = merge %[[VAL_5]]#0 : tuple 22 | // CHECK: %[[VAL_8:.*]]:2 = unpack %[[VAL_7]] : tuple 23 | // CHECK: %[[VAL_9:.*]] = source 24 | // CHECK: %[[VAL_10:.*]] = constant %[[VAL_9]] {value = 0 : i32} : i32 25 | // CHECK: %[[VAL_11:.*]] = mux %[[VAL_2]]#3 {{\[}}%[[VAL_8]]#0, %[[VAL_10]]] : i1, i32 26 | // CHECK: %[[VAL_12:.*]] = pack %[[VAL_11]], %[[VAL_2]]#2 : tuple 27 | // CHECK: %[[VAL_13:.*]] = source 28 | // CHECK: %[[VAL_14:.*]] = constant %[[VAL_13]] {value = 0 : i32} : i32 29 | // CHECK: %[[VAL_15:.*]] = mux %[[VAL_2]]#1 {{\[}}%[[VAL_8]]#1, %[[VAL_14]]] : i1, i32 30 | // CHECK: %[[VAL_16:.*]] = pack %[[VAL_15]], %[[VAL_2]]#0 : tuple 31 | // CHECK: return %[[VAL_12]], %[[VAL_16]] : tuple, tuple 32 | // CHECK: } 33 | 34 | // CHECK-LABEL: handshake.func @split( 35 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, i1>, ...) -> (tuple, tuple) 36 | // CHECK: %[[VAL_1:.*]]:2 = instance @stream_split(%[[VAL_0]]) : (tuple, i1>) -> (tuple, tuple) 37 | // CHECK: return %[[VAL_1]]#0, %[[VAL_1]]#1 : tuple, tuple 38 | // CHECK: } 39 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/map.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake --split-input-file | FileCheck %s 2 | 3 | func.func @map(%in: !stream.stream) -> !stream.stream { 4 | %res = stream.map(%in) : (!stream.stream) -> !stream.stream { 5 | ^0(%val : i32): 6 | %0 = arith.constant 1 : i32 7 | %r = arith.addi %0, %val : i32 8 | stream.yield %r : i32 9 | } 10 | return %res : !stream.stream 11 | } 12 | 13 | // CHECK-LABEL: handshake.func private @stream_map( 14 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 15 | // CHECK: %[[VAL_1:.*]] = source 16 | // CHECK: %[[VAL_2:.*]] = constant %[[VAL_1]] {value = 0 : i32} : i32 17 | // CHECK: %[[VAL_3:.*]] = mux %[[VAL_4:.*]]#1 {{\[}}%[[VAL_5:.*]], %[[VAL_2]]] : i1, i32 18 | // CHECK: %[[VAL_6:.*]]:2 = unpack %[[VAL_0]] : tuple 19 | // CHECK: %[[VAL_4]]:3 = fork [3] %[[VAL_6]]#1 : i1 20 | // CHECK: %[[VAL_7:.*]], %[[VAL_8:.*]] = cond_br %[[VAL_4]]#2, %[[VAL_6]]#0 : i32 21 | // CHECK: sink %[[VAL_7]] : i32 22 | // CHECK: %[[VAL_9:.*]]:2 = fork [2] %[[VAL_8]] : i32 23 | // CHECK: %[[VAL_10:.*]] = join %[[VAL_9]]#1 : i32 24 | // CHECK: %[[VAL_11:.*]] = merge %[[VAL_9]]#0 : i32 25 | // CHECK: %[[VAL_12:.*]] = constant %[[VAL_10]] {value = 1 : i32} : i32 26 | // CHECK: %[[VAL_5]] = arith.addi %[[VAL_12]], %[[VAL_11]] : i32 27 | // CHECK: %[[VAL_13:.*]] = pack %[[VAL_3]], %[[VAL_4]]#0 : tuple 28 | // CHECK: return %[[VAL_13]] : tuple 29 | // CHECK: } 30 | 31 | // CHECK-LABEL: handshake.func @map( 32 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 33 | // CHECK: %[[VAL_1:.*]] = instance @stream_map(%[[VAL_0]]) : (tuple) -> tuple 34 | // CHECK: return %[[VAL_1]] : tuple 35 | // CHECK: } 36 | 37 | // ----- 38 | 39 | func.func @map_multi_block(%in: !stream.stream) -> !stream.stream { 40 | %res = stream.map(%in) : (!stream.stream) -> !stream.stream { 41 | ^0(%val : i32): 42 | %c0 = arith.constant 0 : i32 43 | %cond = arith.cmpi eq, %c0, %val : i32 44 | cf.cond_br %cond, ^1, ^2(%val: i32) 45 | ^1: 46 | %0 = arith.constant 1 : i32 47 | %r = arith.addi %0, %val : i32 48 | cf.br ^2(%r: i32) 49 | ^2(%out: i32): 50 | stream.yield %out : i32 51 | } 52 | return %res : !stream.stream 53 | } 54 | 55 | // CHECK: handshake.func private @[[LABEL:.*]](%{{.*}}: tuple, ...) -> tuple 56 | // CHECK: handshake.func @map_multi_block(%{{.*}}: tuple, ...) -> tuple 57 | -------------------------------------------------------------------------------- /docs/RationaleStream.md: -------------------------------------------------------------------------------- 1 | # Stream Dialect Rationale 2 | 3 | This document describes various design points of the `stream` dialect. 4 | This follows in the spirit of other 5 | [MLIR Rationale docs](https://mlir.llvm.org/docs/Rationale/). 6 | 7 | ## Introduction 8 | 9 | Streaming libraries and abstractions become more popular as languages and tools adopt such concepts. 10 | Furthermore, some high-level synthesis tools provide library support for streaming code, which then can be lowered to hardware. 11 | In the spirit of MLIR and CIRCT, we anticipate that providing higher-level 12 | abstraction in the form of a dialect simplifies further implementation efforts by providing a uniform interface. 13 | 14 | ## Types 15 | 16 | The `stream` dialect introduces a single type that defines a stream by its element types. An element can either be an integer or a tuple of element types. 17 | 18 | Examples: 19 | 20 | ``` 21 | !stream.stream 22 | !stream.stream>> 23 | ``` 24 | 25 | ## Operations 26 | 27 | There are two different kinds of operations: 28 | 1. A set of operations that work directly with streams. These operations all consume and produce a variable amount of streams. 29 | 2. Auxiliary operations that help to work with elements of the stream, e.g., packing or unpacking tuples, yielding elements, etc. 30 | 31 | So far, the `stream` dialect supports the following set of stream operations: `map`, `filter`, `reduce`, and `create`. 32 | The first three expect regions that define the computation to be performed on each stream element. Note that the region arguments differ depending on the operation and the element types of the streams passed in. 33 | 34 | Example: 35 | 36 | ```mlir 37 | %res = stream.map(%in) : (!stream.stream) -> !stream.stream { 38 | ^0(%val : i32): 39 | %0 = arith.constant 1 : i32 40 | %r = arith.addi %0, %val : i32 41 | stream.yield %r : i32 42 | } 43 | } 44 | ``` 45 | 46 | ## Lowering 47 | 48 | One natural target for the streaming abstraction to lower is the handshake dialect. 49 | The handshake dialect is somewhat stable, and the `StandardToHandshake` pass can be reused to lower the regions of the operations. 50 | 51 | The streaming abstraction can be lowered to a task pipelined handshake representation. 52 | Each stream becomes an handshaked value of the element type, and all the operations defined on this stream are applied directly to these values. 53 | Note that certain operations like `filter` and `reduce` can terminate incoming control flow. 54 | 55 | ### End-of-Stream signal 56 | 57 | Some operations, e.g., `reduce`, only produce a result when the incoming stream terminates. 58 | To allow such behavior upon lowering each stream provides an `EOS` signal which is asserted once 59 | the stream is ending. 60 | 61 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/complex/complex.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=complex --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=complex --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=complex --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // CHECK: ** TEST 25 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 26 | 27 | module { 28 | func.func @top(%in: !stream.stream) -> !stream.stream { 29 | %left, %right = stream.split(%in) : (!stream.stream) -> (!stream.stream, !stream.stream) { 30 | ^0(%val : i64): 31 | stream.yield %val, %val : i64, i64 32 | } 33 | 34 | %leftR = stream.reduce(%left) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 35 | ^0(%acc: i64, %val: i64): 36 | %r = arith.addi %acc, %val : i64 37 | stream.yield %r : i64 38 | } 39 | 40 | %rightR = stream.reduce(%right) {initValue = 1 : i64}: (!stream.stream) -> !stream.stream { 41 | ^0(%acc: i64, %val: i64): 42 | %r = arith.muli %acc, %val : i64 43 | stream.yield %r : i64 44 | } 45 | 46 | %out = stream.combine(%leftR, %rightR) : (!stream.stream, !stream.stream) -> (!stream.stream) { 47 | ^0(%val0: i64, %val1: i64): 48 | %0 = arith.addi %val0, %val1 : i64 49 | stream.yield %0 : i64 50 | } 51 | 52 | return %out : !stream.stream 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13.4) 2 | project(circt-stream LANGUAGES CXX C) 3 | 4 | set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON) 5 | 6 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to") 7 | 8 | find_package(LLVM REQUIRED CONFIG) 9 | find_package(MLIR REQUIRED CONFIG) 10 | find_package(CIRCT REQUIRED CONFIG) 11 | 12 | message(STATUS "Using MLIRConfig.cmake in: ${MLIR_DIR}") 13 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 14 | message(STATUS "Using CIRCTConfig.cmake in: ${CIRCT_DIR}") 15 | 16 | set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/bin) 17 | set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/lib) 18 | set(MLIR_BINARY_DIR ${CMAKE_BINARY_DIR}) 19 | 20 | list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}") 21 | list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") 22 | include(TableGen) 23 | include(AddLLVM) 24 | include(AddMLIR) 25 | include(HandleLLVMOptions) 26 | 27 | 28 | set(PROJECT_TOOLS_DIR ${CMAKE_BINARY_DIR}/bin) 29 | 30 | #------------------------------------------------------------------------------- 31 | # Icarus Verilog Configuration 32 | #------------------------------------------------------------------------------- 33 | 34 | # If Icarus Verilog hasn't been explicitly disabled, find it. 35 | option(IVERILOG_DISABLE "Disable the Icarus Verilog tests.") 36 | if (IVERILOG_DISABLE) 37 | message(STATUS "Disabling Icarus Verilog tests.") 38 | else() 39 | find_program(IVERILOG_PATH "iverilog") 40 | if(EXISTS ${IVERILOG_PATH}) 41 | # Find iverilog version. 42 | execute_process(COMMAND ${IVERILOG_PATH} -V 43 | OUTPUT_VARIABLE IVERILOG_VERSION) 44 | 45 | string(REGEX MATCH "Icarus Verilog version (([0-9]+)\.([0-9]+)) \.*" 46 | MATCH ${IVERILOG_VERSION}) 47 | 48 | if (${CMAKE_MATCH_1} LESS 11.0) 49 | message(FATAL_ERROR "CIRCT only supports Icarus Verilog version 11.0 and up. \ 50 | Found version: ${CMAKE_MATCH_1}. You can disable \ 51 | the Icarus Verilog tests with '-DIVERILOG_DISABLE=ON'.") 52 | set(IVERILOG_PATH "") 53 | endif() 54 | message(STATUS "Found iverilog at ${IVERILOG_PATH}.") 55 | else() 56 | set(IVERILOG_PATH "") 57 | message(STATUS "Did not find iverilog.") 58 | endif() 59 | endif() 60 | 61 | 62 | find_package(Python3) 63 | if(Python3_FOUND) 64 | message(STATUS "Found python at ${Python3_EXECUTABLE}") 65 | endif() 66 | 67 | 68 | include_directories(${LLVM_INCLUDE_DIRS}) 69 | include_directories(${MLIR_INCLUDE_DIRS}) 70 | include_directories(${CIRCT_INCLUDE_DIRS}) 71 | 72 | 73 | 74 | include_directories(${PROJECT_SOURCE_DIR}/include) 75 | include_directories(${PROJECT_BINARY_DIR}/include) 76 | link_directories(${LLVM_BUILD_LIBRARY_DIR}) 77 | add_definitions(${LLVM_DEFINITIONS}) 78 | 79 | add_subdirectory(include) 80 | add_subdirectory(lib) 81 | add_subdirectory(test) 82 | add_subdirectory(integration_test) 83 | add_subdirectory(tools) 84 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/reduce.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | func.func @reduce(%in: !stream.stream) -> !stream.stream { 3 | %res = stream.reduce(%in) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 4 | ^0(%acc: i64, %val: i64): 5 | %r = arith.addi %acc, %val : i64 6 | stream.yield %r : i64 7 | } 8 | return %res : !stream.stream 9 | } 10 | 11 | // CHECK-LABEL: handshake.func private @stream_reduce( 12 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 13 | // CHECK: %[[VAL_1:.*]]:2 = unpack %[[VAL_0]] : tuple 14 | // CHECK: %[[VAL_2:.*]]:5 = fork [5] %[[VAL_1]]#1 : i1 15 | // CHECK: %[[VAL_3:.*]] = source 16 | // CHECK: %[[VAL_4:.*]] = constant %[[VAL_3]] {value = 0 : i64} : i64 17 | // CHECK: %[[VAL_5:.*]] = mux %[[VAL_2]]#4 {{\[}}%[[VAL_6:.*]], %[[VAL_4]]] : i1, i64 18 | // CHECK: %[[VAL_7:.*]] = buffer [1] seq %[[VAL_5]] {initValues = [0]} : i64 19 | // CHECK: %[[VAL_8:.*]], %[[VAL_9:.*]] = cond_br %[[VAL_2]]#3, %[[VAL_7]] : i64 20 | // CHECK: %[[VAL_10:.*]]:2 = fork [2] %[[VAL_9]] : i64 21 | // CHECK: %[[VAL_11:.*]]:3 = fork [3] %[[VAL_8]] : i64 22 | // CHECK: %[[VAL_12:.*]], %[[VAL_13:.*]] = cond_br %[[VAL_2]]#1, %[[VAL_2]]#2 : i1 23 | // CHECK: sink %[[VAL_13]] : i1 24 | // CHECK: %[[VAL_14:.*]], %[[VAL_15:.*]] = cond_br %[[VAL_2]]#0, %[[VAL_1]]#0 : i64 25 | // CHECK: sink %[[VAL_14]] : i64 26 | // CHECK: %[[VAL_16:.*]] = join %[[VAL_10]]#1 : i64 27 | // CHECK: sink %[[VAL_16]] : none 28 | // CHECK: %[[VAL_17:.*]] = merge %[[VAL_10]]#0 : i64 29 | // CHECK: %[[VAL_18:.*]] = merge %[[VAL_15]] : i64 30 | // CHECK: %[[VAL_6]] = arith.addi %[[VAL_17]], %[[VAL_18]] : i64 31 | // CHECK: %[[VAL_19:.*]] = join %[[VAL_11]]#2 : i64 32 | // CHECK: %[[VAL_20:.*]] = constant %[[VAL_19]] {value = false} : i1 33 | // CHECK: %[[VAL_21:.*]] = pack %[[VAL_11]]#1, %[[VAL_20]] : tuple 34 | // CHECK: %[[VAL_22:.*]] = pack %[[VAL_11]]#0, %[[VAL_12]] : tuple 35 | // CHECK: %[[VAL_23:.*]] = buffer [2] seq %[[VAL_24:.*]]#1 {initValues = [1, 0]} : i1 36 | // CHECK: %[[VAL_24]]:2 = fork [2] %[[VAL_23]] : i1 37 | // CHECK: %[[VAL_25:.*]] = mux %[[VAL_24]]#0 {{\[}}%[[VAL_21]], %[[VAL_22]]] : i1, tuple 38 | // CHECK: return %[[VAL_25]] : tuple 39 | // CHECK: } 40 | 41 | // CHECK-LABEL: handshake.func @reduce( 42 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 43 | // CHECK: %[[VAL_1:.*]] = instance @stream_reduce(%[[VAL_0]]) : (tuple) -> tuple 44 | // CHECK: return %[[VAL_1]] : tuple 45 | // CHECK: } 46 | -------------------------------------------------------------------------------- /integration_test/lit.site.cfg.py.in: -------------------------------------------------------------------------------- 1 | @LIT_SITE_CFG_IN_HEADER@ 2 | 3 | import sys 4 | 5 | config.host_triple = "@LLVM_HOST_TRIPLE@" 6 | config.target_triple = "@TARGET_TRIPLE@" 7 | config.llvm_src_root = "@LLVM_SOURCE_DIR@" 8 | config.llvm_obj_root = "@LLVM_BINARY_DIR@" 9 | config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" 10 | config.llvm_lib_dir = "@LLVM_LIBRARY_DIR@" 11 | config.llvm_shlib_dir = "@SHLIBDIR@" 12 | config.llvm_shlib_ext = "@SHLIBEXT@" 13 | config.llvm_exe_ext = "@EXEEXT@" 14 | config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" 15 | config.python_executable = "@Python3_EXECUTABLE@" 16 | config.gold_executable = "@GOLD_EXECUTABLE@" 17 | config.ld64_executable = "@LD64_EXECUTABLE@" 18 | config.enable_shared = @ENABLE_SHARED@ 19 | config.enable_assertions = @ENABLE_ASSERTIONS@ 20 | config.targets_to_build = "@TARGETS_TO_BUILD@" 21 | config.native_target = "@LLVM_NATIVE_ARCH@" 22 | config.llvm_bindings = "@LLVM_BINDINGS@".split(' ') 23 | config.host_os = "@HOST_OS@" 24 | config.host_cc = "@HOST_CC@" 25 | config.host_cxx = "@HOST_CXX@" 26 | # Note: ldflags can contain double-quoted paths, so must use single quotes here. 27 | config.host_ldflags = '@HOST_LDFLAGS@' 28 | config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@" 29 | config.llvm_host_triple = '@LLVM_HOST_TRIPLE@' 30 | config.host_arch = "@HOST_ARCH@" 31 | config.mlir_src_root = "@MLIR_SOURCE_DIR@" 32 | config.mlir_obj_root = "@MLIR_BINARY_DIR@" 33 | config.mlir_tools_dir = "@MLIR_TOOLS_DIR@" 34 | config.circt_src_root = "@CIRCT_SOURCE_DIR@" 35 | config.circt_obj_root = "@CIRCT_BINARY_DIR@" 36 | config.circt_tools_dir = "@CIRCT_TOOLS_DIR@" 37 | config.circt_utils_dir = "@CIRCT_UTILS_DIR@" 38 | config.circt_include_dir = "@CIRCT_MAIN_INCLUDE_DIR@" 39 | config.circt_shlib_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@" 40 | config.circt_python_packages_dir = "@CIRCT_PYTHON_PACKAGES_DIR@" 41 | config.project_src_root = "@PROJECT_SOURCE_DIR@" 42 | config.project_obj_root = "@PROJECT_BINARY_DIR@" 43 | config.project_tools_dir = "@PROJECT_TOOLS_DIR@" 44 | config.project_utils_dir = "@PROJECT_UTILS_DIR@" 45 | config.project_include_dir = "@PROJECT_MAIN_INCLUDE_DIR@" 46 | 47 | config.verilator_path = "@VERILATOR_PATH@" 48 | config.esi_cosim_path = "@ESI_COSIM_PATH@" 49 | config.timeout = "@CIRCT_INTEGRATION_TIMEOUT@" 50 | config.iverilog_path = "@IVERILOG_PATH@" 51 | config.esi_capnp = "@ESI_CAPNP@" 52 | 53 | # Support substitution of the tools_dir with user parameters. This is 54 | # used when we can't determine the tool dir at configuration time. 55 | try: 56 | config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params 57 | config.llvm_shlib_dir = config.llvm_shlib_dir % lit_config.params 58 | except KeyError: 59 | e = sys.exc_info()[1] 60 | key, = e.args 61 | lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) 62 | 63 | import lit.llvm 64 | lit.llvm.initialize(lit_config, config) 65 | 66 | # Let the main config do the real work. 67 | lit_config.load_config(config, "@PROJECT_SOURCE_DIR@/integration_test/lit.cfg.py") 68 | -------------------------------------------------------------------------------- /tools/stream-opt.cpp: -------------------------------------------------------------------------------- 1 | //===- stream-opt.cpp -------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #include "circt-stream/Conversion/Passes.h" 10 | #include "circt-stream/Dialect/Stream/StreamDialect.h" 11 | #include "circt-stream/Transform/Passes.h" 12 | #include "circt/InitAllDialects.h" 13 | #include "circt/InitAllPasses.h" 14 | #include "mlir/Dialect/Arith/IR/Arith.h" 15 | #include "mlir/IR/Dialect.h" 16 | #include "mlir/IR/MLIRContext.h" 17 | #include "mlir/InitAllDialects.h" 18 | #include "mlir/InitAllPasses.h" 19 | #include "mlir/Pass/Pass.h" 20 | #include "mlir/Pass/PassManager.h" 21 | #include "mlir/Support/FileUtilities.h" 22 | #include "mlir/Tools/mlir-opt/MlirOptMain.h" 23 | #include "llvm/Support/CommandLine.h" 24 | #include "llvm/Support/InitLLVM.h" 25 | #include "llvm/Support/SourceMgr.h" 26 | #include "llvm/Support/ToolOutputFile.h" 27 | 28 | int main(int argc, char **argv) { 29 | mlir::DialectRegistry registry; 30 | 31 | // TODO only register required dialects 32 | registry.insert(); 33 | registry.insert(); 34 | registry.insert(); 35 | registry.insert(); 36 | registry.insert(); 37 | registry.insert(); 38 | registry.insert(); 39 | 40 | mlir::registerCSEPass(); 41 | mlir::registerSCCPPass(); 42 | mlir::registerInlinerPass(); 43 | mlir::registerCanonicalizerPass(); 44 | mlir::registerSCFToControlFlowPass(); 45 | 46 | // clang-format off 47 | registry.insert< 48 | circt::chirrtl::CHIRRTLDialect, 49 | circt::comb::CombDialect, 50 | circt::firrtl::FIRRTLDialect, 51 | circt::handshake::HandshakeDialect, 52 | circt::llhd::LLHDDialect, 53 | circt::hw::HWDialect, 54 | circt::seq::SeqDialect, 55 | circt::pipeline::PipelineDialect, 56 | circt::sv::SVDialect 57 | >(); 58 | // clang-format on 59 | 60 | circt::registerAffineToPipelinePass(); 61 | circt::registerConvertHWToLLHDPass(); 62 | circt::registerConvertLLHDToLLVMPass(); 63 | circt::registerExportSplitVerilogPass(); 64 | circt::registerExportVerilogPass(); 65 | circt::registerHandshakeRemoveBlockPass(); 66 | circt::registerHandshakeToFIRRTLPass(); 67 | circt::registerHandshakeToHWPass(); 68 | circt::registerLowerFIRRTLToHWPass(); 69 | circt::registerStandardToHandshakePass(); 70 | 71 | circt::registerFlattenMemRefPass(); 72 | circt::registerFlattenMemRefCallsPass(); 73 | 74 | circt::firrtl::registerPasses(); 75 | circt::llhd::initLLHDTransformationPasses(); 76 | circt::seq::registerPasses(); 77 | circt::sv::registerPasses(); 78 | circt::handshake::registerPasses(); 79 | circt::hw::registerPasses(); 80 | 81 | registry.insert(); 82 | 83 | circt_stream::registerConversionPasses(); 84 | circt_stream::registerTransformPasses(); 85 | 86 | return mlir::asMainReturnCode( 87 | mlir::MlirOptMain(argc, argv, "Stream optimizer driver\n", registry)); 88 | } 89 | -------------------------------------------------------------------------------- /.github/workflows/formatting.yml: -------------------------------------------------------------------------------- 1 | name: Check format 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [assigned, opened, synchronize, reopened] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | # Do sanity check (clang-format and python-format) first. 13 | sanity-check: 14 | name: Sanity Check 15 | runs-on: ubuntu-latest 16 | container: 17 | image: ghcr.io/circt/images/circt-ci-build:20220216182244 18 | steps: 19 | - name: Get CIRCT Stream 20 | uses: actions/checkout@v2 21 | with: 22 | fetch-depth: 2 23 | submodules: "false" 24 | 25 | # -------- 26 | # Lint the CIRCT stream C++ code. 27 | # ------- 28 | 29 | # Choose the git commit to diff against for the purposes of linting. 30 | # Since this workflow is triggered on both pushes and pull requests, we 31 | # have to determine if the pull request target branch is set (which it 32 | # will only be on the PR triggered flow). If it's not, then compare 33 | # against the last commit. 34 | - name: choose-commit 35 | if: ${{ always() }} 36 | env: 37 | # Base ref is the target branch, in text form (not hash) 38 | PR_BASE: ${{ github.base_ref }} 39 | run: | 40 | # Run clang-format 41 | if [ -z "$PR_BASE" ]; then 42 | DIFF_COMMIT_NAME="HEAD^" 43 | else 44 | DIFF_COMMIT_NAME="$PR_BASE" 45 | fi 46 | echo "DIFF_COMMIT_NAME=$DIFF_COMMIT_NAME" >> $GITHUB_ENV 47 | 48 | # Since we did a shallow fetch for this repo, we must fetch the commit 49 | # upon which we be diff'ing. The last step set the ref name in the 50 | # $DIFF_COMMIT_NAME environment variable. When running the fetch, resolve 51 | # it to the commit hash and pass that hash along to subsequent steps. 52 | - name: git fetch base commit 53 | continue-on-error: true 54 | run: | 55 | if echo "$DIFF_COMMIT_NAME" | grep -q HEAD; then 56 | DIFF_COMMIT_SHA=$( git rev-parse $DIFF_COMMIT_NAME ) 57 | else 58 | git fetch --recurse-submodules=no origin $DIFF_COMMIT_NAME 59 | DIFF_COMMIT_SHA=$( git rev-parse origin/$DIFF_COMMIT_NAME ) 60 | fi 61 | echo "DIFF_COMMIT=$DIFF_COMMIT_SHA" >> $GITHUB_ENV 62 | 63 | # Run 'git clang-format', comparing against the target commit hash. If 64 | # clang-format fixed anything, fail and output a patch. 65 | - name: clang-format 66 | if: ${{ always() }} 67 | run: | 68 | # Run clang-format 69 | git clang-format $DIFF_COMMIT 70 | git diff --ignore-submodules > clang-format.patch 71 | if [ -s clang-format.patch ]; then 72 | echo "Clang-format found formatting problems in the following " \ 73 | "files. See diff in the clang-format.patch artifact." 74 | git diff --ignore-submodules --name-only 75 | git checkout . 76 | exit 1 77 | fi 78 | echo "Clang-format found no formatting problems" 79 | exit 0 80 | 81 | - name: clang format patches display 82 | if: ${{ failure() }} 83 | continue-on-error: true 84 | run: | 85 | # Display patches 86 | if [ ! -z clang-format.patch ]; then 87 | echo "Clang-format patch" 88 | echo "================" 89 | cat clang-format.patch 90 | echo "================" 91 | fi 92 | -------------------------------------------------------------------------------- /test/Dialect/Stream/canonicalize.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s -canonicalize -allow-unregistered-dialect | FileCheck %s 2 | 3 | // CHECK-LABEL: func.func @pack_unpack( 4 | // CHECK-SAME: %[[a:.*]]: i32, 5 | // CHECK-SAME: %[[b:.*]]: i64) -> i32 { 6 | // CHECK: return %[[a]] : i32 7 | // CHECK: } 8 | func.func @pack_unpack(%a: i32, %b: i64) -> i32 { 9 | %res = stream.pack %a, %b : tuple 10 | %a2, %b2 = stream.unpack %res : tuple 11 | return %a2 : i32 12 | } 13 | 14 | // CHECK-LABEL: func.func @pack_unpack2( 15 | // CHECK-SAME: %[[a:.*]]: i32, 16 | // CHECK-SAME: %[[b:.*]]: i64) -> tuple { 17 | // CHECK: %[[res:.*]] = stream.pack %[[a]], %[[b]] : tuple 18 | // CHECK: return %[[res]] : tuple 19 | // CHECK: } 20 | func.func @pack_unpack2(%a: i32, %b: i64) -> tuple { 21 | %res = stream.pack %a, %b : tuple 22 | %a2, %b2 = stream.unpack %res : tuple 23 | return %res : tuple 24 | } 25 | 26 | // CHECK-LABEL: func.func @pack_unpack_negative( 27 | // CHECK-SAME: %[[a:.*]]: i32, 28 | // CHECK-SAME: %[[other_res:.*]]: tuple) { 29 | // CHECK: %[[res:.*]] = stream.pack %[[a]], %[[a]] : tuple 30 | // CHECK: %[[ab:.*]]:2 = stream.unpack %[[other_res]] : tuple 31 | // CHECK: "test.foo"(%[[res]], %[[ab]]#0, %[[ab]]#1) : (tuple, i32, i64) -> () 32 | // CHECK: return 33 | // CHECK: } 34 | 35 | func.func @pack_unpack_negative(%a: i32, %other_res: tuple) { 36 | %res = stream.pack %a, %a : tuple 37 | %a2, %b2 = stream.unpack %other_res : tuple 38 | "test.foo"(%res, %a2, %b2) : (tuple, i32, i64) -> () 39 | return 40 | } 41 | 42 | // CHECK-LABEL: func.func @unpack_pack( 43 | // CHECK-SAME: %[[res:.*]]: tuple) -> tuple { 44 | // CHECK: return %[[res]] : tuple 45 | // CHECK: } 46 | func.func @unpack_pack(%res: tuple) -> tuple { 47 | %a, %b = stream.unpack %res : tuple 48 | %res2 = stream.pack %a, %b : tuple 49 | return %res2 : tuple 50 | } 51 | 52 | // CHECK-LABEL: func.func @unpack_pack2( 53 | // CHECK-SAME: %[[res:.*]]: tuple) -> i32 { 54 | // CHECK: %[[a:.*]]:2 = stream.unpack %[[res]] : tuple 55 | // CHECK: return %[[a]]#0 : i32 56 | // CHECK: } 57 | func.func @unpack_pack2(%res: tuple) -> i32 { 58 | %a, %b = stream.unpack %res : tuple 59 | %res2 = stream.pack %a, %b : tuple 60 | return %a : i32 61 | } 62 | 63 | // CHECK-LABEL: func.func @unpack_pack_negative( 64 | // CHECK-SAME: %[[arg:.*]]: i32, 65 | // CHECK-SAME: %[[other_res:.*]]: tuple) { 66 | // CHECK: %[[ab:.*]]:2 = stream.unpack %[[other_res]] : tuple 67 | // CHECK: %[[res:.*]] = stream.pack %[[arg]], %[[arg]] : tuple 68 | // CHECK: "test.foo"(%[[res]], %[[ab]]#0, %[[ab]]#1) : (tuple, i32, i64) -> () 69 | // CHECK: return 70 | // CHECK: } 71 | 72 | func.func @unpack_pack_negative(%arg: i32, %other_res: tuple) { 73 | %a, %b = stream.unpack %other_res : tuple 74 | %res = stream.pack %arg, %arg : tuple 75 | "test.foo"(%res, %a, %b) : (tuple, i32, i64) -> () 76 | return 77 | } 78 | -------------------------------------------------------------------------------- /integration_test/lit.cfg.py: -------------------------------------------------------------------------------- 1 | # -*- Python -*- 2 | 3 | import os 4 | import platform 5 | import re 6 | import shutil 7 | import subprocess 8 | import tempfile 9 | import warnings 10 | 11 | import lit.formats 12 | import lit.util 13 | 14 | from lit.llvm import llvm_config 15 | from lit.llvm.subst import ToolSubst 16 | from lit.llvm.subst import FindTool 17 | 18 | # Configuration file for the 'lit' test runner. 19 | 20 | # name: The name of this test suite. 21 | config.name = 'Stream' 22 | 23 | config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) 24 | 25 | # suffixes: A list of file extensions to treat as test files. 26 | config.suffixes = ['.td', '.mlir', '.ll', '.fir', '.sv', '.py', '.tcl'] 27 | 28 | # test_source_root: The root path where tests are located. 29 | config.test_source_root = os.path.dirname(__file__) 30 | 31 | # test_exec_root: The root path where tests should be run. 32 | config.test_exec_root = os.path.join(config.circt_obj_root, 'test') 33 | 34 | config.substitutions.append(('%PATH%', config.environment['PATH'])) 35 | config.substitutions.append(('%shlibext', config.llvm_shlib_ext)) 36 | config.substitutions.append(('%shlibdir', config.circt_shlib_dir)) 37 | config.substitutions.append(('%INC%', config.circt_include_dir)) 38 | config.substitutions.append( 39 | ('%TCL_PATH%', config.circt_src_root + '/build/lib/Bindings/Tcl/')) 40 | config.substitutions.append(('%PROJECT_SOURCE%', config.project_src_root)) 41 | 42 | llvm_config.with_system_environment(['HOME', 'INCLUDE', 'LIB', 'TMP', 'TEMP']) 43 | 44 | llvm_config.use_default_substitutions() 45 | 46 | # Set the timeout, if requested. 47 | if config.timeout is not None and config.timeout != "": 48 | lit_config.maxIndividualTestTime = int(config.timeout) 49 | 50 | # excludes: A list of directories to exclude from the testsuite. The 'Inputs' 51 | # subdirectories contain auxiliary inputs for various tests in their parent 52 | # directories. 53 | config.excludes = [ 54 | 'Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt', 'lit.cfg.py', 55 | 'lit.local.cfg.py' 56 | ] 57 | 58 | # test_source_root: The root path where tests are located. 59 | config.test_source_root = os.path.dirname(__file__) 60 | 61 | # test_exec_root: The root path where tests should be run. 62 | config.test_exec_root = os.path.join(config.circt_obj_root, 'integration_test') 63 | 64 | # Tweak the PATH to include the tools dir. 65 | llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True) 66 | # Substitute '%l' with the path to the build lib dir. 67 | 68 | tool_dirs = [ 69 | config.circt_tools_dir, config.circt_utils_dir, config.mlir_tools_dir, 70 | config.llvm_tools_dir, config.project_tools_dir 71 | ] 72 | tools = [ 73 | 'stream-opt', 74 | 'firtool', 75 | 'circt-rtl-sim.py', 76 | ] 77 | 78 | # Enable python if its path was configured 79 | if config.python_executable != "": 80 | tool_dirs.append(os.path.dirname(config.python_executable)) 81 | config.available_features.add('python') 82 | config.substitutions.append(('%PYTHON%', config.python_executable)) 83 | 84 | # Enable Icarus Verilog as a fallback if no other ieee-sim was detected. 85 | if config.iverilog_path != "": 86 | tool_dirs.append(os.path.dirname(config.iverilog_path)) 87 | tools.append('iverilog') 88 | tools.append('vvp') 89 | config.available_features.add('iverilog') 90 | config.substitutions.append(('%iverilog', config.iverilog_path)) 91 | 92 | llvm_config.add_tool_substitutions(tools, tool_dirs) 93 | 94 | # cocotb availability 95 | try: 96 | import cocotb 97 | import cocotb_test 98 | config.available_features.add('cocotb') 99 | except ImportError: 100 | pass 101 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/combined.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake | FileCheck %s 2 | 3 | func.func @combined(%in: !stream.stream) -> !stream.stream { 4 | %tmp = stream.map(%in) : (!stream.stream) -> !stream.stream { 5 | ^0(%val : i512): 6 | %0 = arith.constant 42 : i512 7 | %r = arith.addi %0, %val : i512 8 | stream.yield %r : i512 9 | } 10 | %res = stream.filter(%tmp) : (!stream.stream) -> !stream.stream { 11 | ^bb0(%val: i512): 12 | %c0_i512 = arith.constant 0 : i512 13 | %0 = arith.cmpi sgt, %val, %c0_i512 : i512 14 | stream.yield %0 : i1 15 | } 16 | return %res : !stream.stream 17 | } 18 | 19 | // CHECK-LABEL: handshake.func private @stream_filter( 20 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 21 | // CHECK: %[[VAL_1:.*]]:2 = fork [2] %[[VAL_0]] : tuple 22 | // CHECK: %[[VAL_2:.*]]:2 = unpack %[[VAL_1]]#1 : tuple 23 | // CHECK: %[[VAL_3:.*]]:4 = fork [4] %[[VAL_2]]#1 : i1 24 | // CHECK: %[[VAL_4:.*]], %[[VAL_5:.*]] = cond_br %[[VAL_3]]#3, %[[VAL_2]]#0 : i512 25 | // CHECK: sink %[[VAL_4]] : i512 26 | // CHECK: %[[VAL_6:.*]]:2 = fork [2] %[[VAL_5]] : i512 27 | // CHECK: %[[VAL_7:.*]] = join %[[VAL_6]]#1 : i512 28 | // CHECK: %[[VAL_8:.*]] = merge %[[VAL_6]]#0 : i512 29 | // CHECK: %[[VAL_9:.*]] = constant %[[VAL_7]] {value = 0 : i512} : i512 30 | // CHECK: %[[VAL_10:.*]] = arith.cmpi sgt, %[[VAL_8]], %[[VAL_9]] : i512 31 | // CHECK: %[[VAL_11:.*]], %[[VAL_12:.*]] = cond_br %[[VAL_3]]#1, %[[VAL_3]]#2 : i1 32 | // CHECK: sink %[[VAL_12]] : i1 33 | // CHECK: %[[VAL_13:.*]] = mux %[[VAL_3]]#0 {{\[}}%[[VAL_10]], %[[VAL_11]]] : i1, i1 34 | // CHECK: %[[VAL_14:.*]], %[[VAL_15:.*]] = cond_br %[[VAL_13]], %[[VAL_1]]#0 : tuple 35 | // CHECK: sink %[[VAL_15]] : tuple 36 | // CHECK: return %[[VAL_14]] : tuple 37 | // CHECK: } 38 | 39 | // CHECK-LABEL: handshake.func private @stream_map( 40 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 41 | // CHECK: %[[VAL_1:.*]] = source 42 | // CHECK: %[[VAL_2:.*]] = constant %[[VAL_1]] {value = 0 : i512} : i512 43 | // CHECK: %[[VAL_3:.*]] = mux %[[VAL_4:.*]]#1 {{\[}}%[[VAL_5:.*]], %[[VAL_2]]] : i1, i512 44 | // CHECK: %[[VAL_6:.*]]:2 = unpack %[[VAL_0]] : tuple 45 | // CHECK: %[[VAL_4]]:3 = fork [3] %[[VAL_6]]#1 : i1 46 | // CHECK: %[[VAL_7:.*]], %[[VAL_8:.*]] = cond_br %[[VAL_4]]#2, %[[VAL_6]]#0 : i512 47 | // CHECK: sink %[[VAL_7]] : i512 48 | // CHECK: %[[VAL_9:.*]]:2 = fork [2] %[[VAL_8]] : i512 49 | // CHECK: %[[VAL_10:.*]] = join %[[VAL_9]]#1 : i512 50 | // CHECK: %[[VAL_11:.*]] = merge %[[VAL_9]]#0 : i512 51 | // CHECK: %[[VAL_12:.*]] = constant %[[VAL_10]] {value = 42 : i512} : i512 52 | // CHECK: %[[VAL_5]] = arith.addi %[[VAL_12]], %[[VAL_11]] : i512 53 | // CHECK: %[[VAL_13:.*]] = pack %[[VAL_3]], %[[VAL_4]]#0 : tuple 54 | // CHECK: return %[[VAL_13]] : tuple 55 | // CHECK: } 56 | 57 | // CHECK-LABEL: handshake.func @combined( 58 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 59 | // CHECK: %[[VAL_1:.*]] = instance @stream_map(%[[VAL_0]]) : (tuple) -> tuple 60 | // CHECK: %[[VAL_2:.*]] = instance @stream_filter(%[[VAL_1]]) : (tuple) -> tuple 61 | // CHECK: return %[[VAL_2]] : tuple 62 | // CHECK: } 63 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/stream-stats/stream-stats.mlir: -------------------------------------------------------------------------------- 1 | // REQUIRES: cocotb, iverilog 2 | 3 | // RUN: stream-opt %s --convert-stream-to-handshake \ 4 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 5 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 6 | // RUN: --handshake-insert-buffers=strategy=all --lower-handshake-to-firrtl | \ 7 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 8 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=stream-stats --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 9 | 10 | // RUN: stream-opt %s --convert-stream-to-handshake \ 11 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 12 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 13 | // RUN: --handshake-insert-buffers=strategy=allFIFO --lower-handshake-to-firrtl | \ 14 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 15 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=stream-stats --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 16 | 17 | // RUN: stream-opt %s --convert-stream-to-handshake \ 18 | // RUN: --canonicalize='top-down=true region-simplify=true' \ 19 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 20 | // RUN: --handshake-insert-buffers=strategy=cycles --lower-handshake-to-firrtl | \ 21 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 22 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=stream-stats --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 23 | 24 | // RUN: stream-opt %s --convert-stream-to-handshake \ 25 | // RUN: --handshake-materialize-forks-sinks --canonicalize \ 26 | // RUN: --custom-buffer-insertion --lower-handshake-to-firrtl | \ 27 | // RUN: firtool --format=mlir --lowering-options=disallowLocalVariables --verilog > %t.sv && \ 28 | // RUN: %PYTHON% %S/../cocotb_driver.py --objdir=%t.sv.d/ --topLevel=top --pythonModule=stream-stats --pythonFolder=%S %t.sv 2>&1 | FileCheck %s 29 | 30 | // CHECK: ** TEST 31 | // CHECK: ** TESTS=[[N:.*]] PASS=[[N]] FAIL=0 SKIP=0 32 | 33 | !T = tuple 34 | module { 35 | func.func @top(%in: !stream.stream) -> (!stream.stream, !stream.stream) { 36 | %data, %copy = stream.split(%in) : (!stream.stream) -> (!stream.stream, !stream.stream) { 37 | ^0(%val : !T): 38 | stream.yield %val, %val : !T, !T 39 | } 40 | 41 | %maxE = stream.map(%copy) : (!stream.stream) -> (!stream.stream) { 42 | ^0(%val : !T): 43 | %e:8 = stream.unpack %val : !T 44 | 45 | %c0 = arith.cmpi slt, %e#0, %e#1 : i64 46 | %t0 = arith.select %c0, %e#0, %e#1 : i64 47 | %c1 = arith.cmpi slt, %e#2, %e#3 : i64 48 | %t1 = arith.select %c1, %e#2, %e#3 : i64 49 | %c2 = arith.cmpi slt, %e#4, %e#5 : i64 50 | %t2 = arith.select %c2, %e#4, %e#5 : i64 51 | %c3 = arith.cmpi slt, %e#6, %e#7 : i64 52 | %t3 = arith.select %c3, %e#6, %e#7 : i64 53 | 54 | %c4 = arith.cmpi slt, %t0, %t1 : i64 55 | %t4 = arith.select %c4, %t0, %t1 : i64 56 | %c5 = arith.cmpi slt, %t2, %t3 : i64 57 | %t5 = arith.select %c5, %t2, %t3 : i64 58 | 59 | %c6 = arith.cmpi slt, %t4, %t5 : i64 60 | %t6 = arith.select %c6, %t4, %t5 : i64 61 | 62 | stream.yield %t6 : i64 63 | } 64 | 65 | %max = stream.reduce(%maxE) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 66 | ^0(%acc: i64, %val: i64): 67 | %pred = arith.cmpi slt, %acc, %val : i64 68 | %newAcc = arith.select %pred, %acc, %val : i64 69 | stream.yield %newAcc : i64 70 | } 71 | 72 | return %data, %max: !stream.stream, !stream.stream 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/cocotb_driver.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import subprocess 4 | import sys 5 | import re 6 | from pathlib import Path 7 | from cocotb_test.simulator import run 8 | 9 | 10 | def parseArgs(args): 11 | argparser = argparse.ArgumentParser(description="COCOTB driver for CIRCT") 12 | 13 | argparser.add_argument("--objdir", 14 | type=str, 15 | help="Select a directoy in which to run this test." + 16 | " Must be different from other tests in the same" + 17 | " directory. Defaults to 'sources[0].d'.") 18 | 19 | argparser.add_argument("--topLevel", 20 | type=str, 21 | help="Name of the top level verilog module.") 22 | 23 | argparser.add_argument("--simulator", 24 | choices=['icarus'], 25 | default="icarus", 26 | help="Name of the simulator to use.") 27 | 28 | argparser.add_argument("--pythonModule", 29 | type=str, 30 | required=True, 31 | help="Name of the python module.") 32 | 33 | argparser.add_argument("--pythonFolder", 34 | type=str, 35 | default=os.getcwd(), 36 | help="The folder where the cocotb test file is.") 37 | 38 | argparser.add_argument( 39 | "sources", 40 | nargs="+", 41 | help="The list of verilog source files to be included.") 42 | 43 | return argparser.parse_args(args[1:]) 44 | 45 | 46 | class _IVerilogHandler: 47 | """ Class for handling icarus-verilog specific commands and patching.""" 48 | 49 | def __init__(self): 50 | # Ensure that iverilog is available in path and it is at least iverilog v11 51 | try: 52 | out = subprocess.check_output(["iverilog", "-V"]) 53 | except subprocess.CalledProcessError: 54 | raise Exception("iverilog not found in path") 55 | 56 | # find the 'Icarus Verilog version #' string and extract the version number 57 | # using a regex 58 | ver_re = r"Icarus Verilog version (\d+\.\d+)" 59 | ver_match = re.search(ver_re, out.decode("utf-8")) 60 | if ver_match is None: 61 | raise Exception("Could not find Icarus Verilog version") 62 | ver = ver_match.group(1) 63 | if float(ver) < 11: 64 | raise Exception(f"Icarus Verilog version must be >= 11, got {ver}") 65 | 66 | def extra_compile_args(self, objDir): 67 | # If no timescale is defined in the source code, icarus assumes a 68 | # timescale of '1'. This prevents cocotb from creating small timescale clocks. 69 | # Since a timescale is not emitted by default from export-verilog, make our 70 | # lives easier and create a minimum timescale through the command-line. 71 | cmd_file = os.path.join(objDir, "cmds.f") 72 | with open(cmd_file, "w+") as f: 73 | f.write("+timescale+1ns/1ps") 74 | 75 | return [f"-f{cmd_file}"] 76 | 77 | 78 | def main(): 79 | args = parseArgs(sys.argv) 80 | sources = [os.path.abspath(s) for s in args.sources] 81 | args.sources = sources 82 | 83 | if args.objdir is not None: 84 | objDir = args.objdir 85 | else: 86 | objDir = f"{os.path.basename(args.sources[0])}.d" 87 | objDir = os.path.abspath(objDir) 88 | if not os.path.exists(objDir): 89 | os.mkdir(objDir) 90 | os.chdir(objDir) 91 | 92 | # Ensure that system has 'make' available: 93 | try: 94 | subprocess.check_output(["make", "-v"]) 95 | except subprocess.CalledProcessError: 96 | raise Exception( 97 | "'make' is not available, and is required to run cocotb tests.") 98 | 99 | try: 100 | if args.simulator == "icarus": 101 | simhandler = _IVerilogHandler() 102 | else: 103 | raise Exception(f"Unknown simulator: {simulator}") 104 | except Exception as e: 105 | raise Exception(f"Failed to initialize simulator handler: {e}") 106 | 107 | # Simulator-specific extra compile args. 108 | compileArgs = [] 109 | if simhandler: 110 | compileArgs += simhandler.extra_compile_args(objDir) 111 | 112 | testmodule = "test_" + args.topLevel 113 | run(simulator=args.simulator, 114 | module=args.pythonModule, 115 | toplevel=args.topLevel, 116 | toplevel_lang="verilog", 117 | verilog_sources=sources, 118 | python_search=[args.pythonFolder], 119 | work_dir=objDir, 120 | compile_args=compileArgs) 121 | 122 | 123 | if __name__ == "__main__": 124 | main() 125 | -------------------------------------------------------------------------------- /test/Conversion/StreamToHandshake/registers.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --convert-stream-to-handshake --split-input-file | FileCheck %s 2 | 3 | // CHECK-LABEL: handshake.func private @stream_map( 4 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 5 | // CHECK: %[[VAL_1:.*]] = source 6 | // CHECK: %[[VAL_2:.*]] = constant %[[VAL_1]] {value = 0 : i32} : i32 7 | // CHECK: %[[VAL_3:.*]] = mux %[[VAL_4:.*]]#1 {{\[}}%[[VAL_5:.*]], %[[VAL_2]]] : i1, i32 8 | // CHECK: %[[VAL_6:.*]]:2 = unpack %[[VAL_0]] : tuple 9 | // CHECK: %[[VAL_4]]:5 = fork [5] %[[VAL_6]]#1 : i1 10 | // CHECK: %[[VAL_7:.*]], %[[VAL_8:.*]] = cond_br %[[VAL_4]]#4, %[[VAL_6]]#0 : i32 11 | // CHECK: sink %[[VAL_7]] : i32 12 | // CHECK: %[[VAL_9:.*]]:2 = fork [2] %[[VAL_8]] : i32 13 | // CHECK: %[[VAL_10:.*]] = source 14 | // CHECK: %[[VAL_11:.*]] = constant %[[VAL_10]] {value = 0 : i32} : i32 15 | // CHECK: %[[VAL_12:.*]] = mux %[[VAL_4]]#3 {{\[}}%[[VAL_13:.*]], %[[VAL_11]]] : i1, i32 16 | // CHECK: %[[VAL_14:.*]] = buffer [1] seq %[[VAL_12]] {initValues = [0]} : i32 17 | // CHECK: %[[VAL_15:.*]], %[[VAL_16:.*]] = cond_br %[[VAL_4]]#2, %[[VAL_14]] : i32 18 | // CHECK: sink %[[VAL_15]] : i32 19 | // CHECK: %[[VAL_17:.*]] = join %[[VAL_9]]#1 : i32 20 | // CHECK: sink %[[VAL_17]] : none 21 | // CHECK: %[[VAL_5]] = merge %[[VAL_9]]#0 : i32 22 | // CHECK: %[[VAL_13]] = merge %[[VAL_16]] : i32 23 | // CHECK: %[[VAL_18:.*]] = pack %[[VAL_3]], %[[VAL_4]]#0 : tuple 24 | // CHECK: return %[[VAL_18]] : tuple 25 | // CHECK: } 26 | 27 | // CHECK-LABEL: handshake.func @single_reg( 28 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 29 | // CHECK: %[[VAL_1:.*]] = instance @stream_map(%[[VAL_0]]) : (tuple) -> tuple 30 | // CHECK: return %[[VAL_1]] : tuple 31 | // CHECK: } 32 | 33 | func.func @single_reg(%in: !stream.stream) -> !stream.stream { 34 | %res = stream.map(%in) {registers = [0 : i32]}: (!stream.stream) -> !stream.stream { 35 | ^0(%val : i32, %reg: i32): 36 | stream.yield %val, %reg : i32, i32 37 | } 38 | return %res: !stream.stream 39 | } 40 | 41 | // ----- 42 | 43 | // CHECK-LABEL: handshake.func private @stream_filter( 44 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 45 | // CHECK: %[[VAL_1:.*]]:2 = fork [2] %[[VAL_0]] : tuple 46 | // CHECK: %[[VAL_2:.*]]:2 = unpack %[[VAL_1]]#1 : tuple 47 | // CHECK: %[[VAL_3:.*]]:8 = fork [8] %[[VAL_2]]#1 : i1 48 | // CHECK: %[[VAL_4:.*]], %[[VAL_5:.*]] = cond_br %[[VAL_3]]#7, %[[VAL_2]]#0 : i32 49 | // CHECK: sink %[[VAL_4]] : i32 50 | // CHECK: %[[VAL_6:.*]]:2 = fork [2] %[[VAL_5]] : i32 51 | // CHECK: %[[VAL_7:.*]] = source 52 | // CHECK: %[[VAL_8:.*]] = constant %[[VAL_7]] {value = false} : i1 53 | // CHECK: %[[VAL_9:.*]] = mux %[[VAL_3]]#6 {{\[}}%[[VAL_10:.*]], %[[VAL_8]]] : i1, i1 54 | // CHECK: %[[VAL_11:.*]] = buffer [1] seq %[[VAL_9]] {initValues = [0]} : i1 55 | // CHECK: %[[VAL_12:.*]], %[[VAL_13:.*]] = cond_br %[[VAL_3]]#5, %[[VAL_11]] : i1 56 | // CHECK: sink %[[VAL_12]] : i1 57 | // CHECK: %[[VAL_14:.*]] = source 58 | // CHECK: %[[VAL_15:.*]] = constant %[[VAL_14]] {value = true} : i1 59 | // CHECK: %[[VAL_16:.*]] = mux %[[VAL_3]]#4 {{\[}}%[[VAL_17:.*]]#1, %[[VAL_15]]] : i1, i1 60 | // CHECK: %[[VAL_18:.*]] = buffer [1] seq %[[VAL_16]] {initValues = [-1]} : i1 61 | // CHECK: %[[VAL_19:.*]], %[[VAL_20:.*]] = cond_br %[[VAL_3]]#3, %[[VAL_18]] : i1 62 | // CHECK: sink %[[VAL_19]] : i1 63 | // CHECK: %[[VAL_21:.*]] = join %[[VAL_6]]#1 : i32 64 | // CHECK: sink %[[VAL_21]] : none 65 | // CHECK: %[[VAL_22:.*]] = merge %[[VAL_6]]#0 : i32 66 | // CHECK: sink %[[VAL_22]] : i32 67 | // CHECK: %[[VAL_23:.*]] = merge %[[VAL_13]] : i1 68 | // CHECK: %[[VAL_17]]:2 = fork [2] %[[VAL_23]] : i1 69 | // CHECK: %[[VAL_10]] = merge %[[VAL_20]] : i1 70 | // CHECK: %[[VAL_24:.*]], %[[VAL_25:.*]] = cond_br %[[VAL_3]]#1, %[[VAL_3]]#2 : i1 71 | // CHECK: sink %[[VAL_25]] : i1 72 | // CHECK: %[[VAL_26:.*]] = mux %[[VAL_3]]#0 {{\[}}%[[VAL_17]]#0, %[[VAL_24]]] : i1, i1 73 | // CHECK: %[[VAL_27:.*]], %[[VAL_28:.*]] = cond_br %[[VAL_26]], %[[VAL_1]]#0 : tuple 74 | // CHECK: sink %[[VAL_28]] : tuple 75 | // CHECK: return %[[VAL_27]] : tuple 76 | // CHECK: } 77 | 78 | // CHECK-LABEL: handshake.func @multiple_regs( 79 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, ...) -> tuple 80 | // CHECK: %[[VAL_1:.*]] = instance @stream_filter(%[[VAL_0]]) : (tuple) -> tuple 81 | // CHECK: return %[[VAL_1]] : tuple 82 | // CHECK: } 83 | 84 | 85 | func.func @multiple_regs(%in: !stream.stream) -> !stream.stream { 86 | %res = stream.filter(%in) {registers = [0 : i1, 1 : i1]}: (!stream.stream) -> !stream.stream { 87 | ^0(%val : i32, %reg0: i1, %reg1 : i1): 88 | stream.yield %reg0, %reg1, %reg0 : i1, i1, i1 89 | } 90 | return %res: !stream.stream 91 | } 92 | -------------------------------------------------------------------------------- /test/Dialect/Stream/reg-ops.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --mlir-print-op-generic | stream-opt | FileCheck %s 2 | 3 | // CHECK-LABEL: func.func @map( 4 | // CHECK-SAME: %[[VAL_0:.*]]: !stream.stream) -> !stream.stream { 5 | // CHECK: %[[VAL_1:.*]] = stream.map(%[[VAL_0]]) {registers = [0 : i32]} : (!stream.stream) -> !stream.stream { 6 | // CHECK: ^bb0(%[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32): 7 | // CHECK: %[[VAL_4:.*]] = arith.addi %[[VAL_2]], %[[VAL_3]] : i32 8 | // CHECK: stream.yield %[[VAL_4]], %[[VAL_4]] : i32, i32 9 | // CHECK: } 10 | // CHECK: return %[[VAL_5:.*]] : !stream.stream 11 | // CHECK: } 12 | 13 | func.func @map(%in: !stream.stream) -> !stream.stream { 14 | %res = stream.map(%in) {registers = [0 : i32]}: (!stream.stream) -> !stream.stream { 15 | ^0(%val : i32, %reg: i32): 16 | %nReg = arith.addi %val, %reg : i32 17 | stream.yield %nReg, %nReg : i32, i32 18 | } 19 | return %res : !stream.stream 20 | } 21 | 22 | // CHECK-LABEL: func.func @filter( 23 | // CHECK-SAME: %[[VAL_0:.*]]: !stream.stream) -> !stream.stream { 24 | // CHECK: %[[VAL_1:.*]] = stream.filter(%[[VAL_0]]) {registers = [false]} : (!stream.stream) -> !stream.stream { 25 | // CHECK: ^bb0(%[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i1): 26 | // CHECK: %[[VAL_4:.*]] = arith.constant true 27 | // CHECK: %[[VAL_5:.*]] = arith.xori %[[VAL_4]], %[[VAL_3]] : i1 28 | // CHECK: stream.yield %[[VAL_5]], %[[VAL_5]] : i1, i1 29 | // CHECK: } 30 | // CHECK: return %[[VAL_6:.*]] : !stream.stream 31 | // CHECK: } 32 | 33 | func.func @filter(%in: !stream.stream) -> !stream.stream { 34 | %res = stream.filter(%in) {registers = [0 : i1]}: (!stream.stream) -> !stream.stream { 35 | ^0(%val : i32, %reg: i1): 36 | %c1 = arith.constant 1 : i1 37 | %nReg = arith.xori %c1, %reg : i1 38 | stream.yield %nReg, %nReg : i1, i1 39 | } 40 | return %res : !stream.stream 41 | } 42 | 43 | // CHECK-LABEL: func.func @reduce( 44 | // CHECK-SAME: %[[VAL_0:.*]]: !stream.stream) -> !stream.stream { 45 | // CHECK: %[[VAL_1:.*]] = stream.reduce(%[[VAL_0]]) {initValue = 0 : i64, registers = [0]} : (!stream.stream) -> !stream.stream { 46 | // CHECK: ^bb0(%[[VAL_2:.*]]: i64, %[[VAL_3:.*]]: i64, %[[VAL_4:.*]]: i64): 47 | // CHECK: %[[VAL_5:.*]] = arith.addi %[[VAL_2]], %[[VAL_3]] : i64 48 | // CHECK: stream.yield %[[VAL_5]], %[[VAL_4]] : i64, i64 49 | // CHECK: } 50 | // CHECK: return %[[VAL_6:.*]] : !stream.stream 51 | // CHECK: } 52 | 53 | func.func @reduce(%in: !stream.stream) -> !stream.stream { 54 | %res = stream.reduce(%in) {initValue = 0 : i64, registers = [0 : i64]}: (!stream.stream) -> !stream.stream { 55 | ^0(%acc: i64, %val: i64, %reg: i64): 56 | %r = arith.addi %acc, %val : i64 57 | stream.yield %r, %reg : i64, i64 58 | } 59 | return %res : !stream.stream 60 | } 61 | 62 | // CHECK-LABEL: func.func @split( 63 | // CHECK-SAME: %[[VAL_0:.*]]: !stream.stream) -> (!stream.stream, !stream.stream) { 64 | // CHECK: %[[VAL_1:.*]]:2 = stream.split(%[[VAL_0]]) {registers = [0, true, 42 : i32]} : (!stream.stream) -> (!stream.stream, !stream.stream) { 65 | // CHECK: ^bb0(%[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i64, %[[VAL_4:.*]]: i1, %[[VAL_5:.*]]: i32): 66 | // CHECK: stream.yield %[[VAL_2]], %[[VAL_2]], %[[VAL_3]], %[[VAL_4]], %[[VAL_5]] : i32, i32, i64, i1, i32 67 | // CHECK: } 68 | // CHECK: return %[[VAL_6:.*]]#0, %[[VAL_6]]#1 : !stream.stream, !stream.stream 69 | // CHECK: } 70 | 71 | func.func @split(%in: !stream.stream) -> (!stream.stream, !stream.stream) { 72 | %res0, %res1 = stream.split(%in) {registers = [0 : i64, 1 : i1, 42 : i32]} : (!stream.stream) -> (!stream.stream, !stream.stream) { 73 | ^0(%val: i32, %reg0: i64, %reg1 : i1, %reg2: i32): 74 | stream.yield %val, %val, %reg0, %reg1, %reg2 : i32, i32, i64, i1, i32 75 | } 76 | return %res0, %res1 : !stream.stream, !stream.stream 77 | } 78 | 79 | // CHECK-LABEL: func.func @combine( 80 | // CHECK-SAME: %[[VAL_0:.*]]: !stream.stream, 81 | // CHECK-SAME: %[[VAL_1:.*]]: !stream.stream) -> !stream.stream> { 82 | // CHECK: %[[VAL_2:.*]] = stream.combine(%[[VAL_0]], %[[VAL_1]]) {registers = [false]} : (!stream.stream, !stream.stream) -> !stream.stream> { 83 | // CHECK: ^bb0(%[[VAL_3:.*]]: i32, %[[VAL_4:.*]]: i32, %[[VAL_5:.*]]: i1): 84 | // CHECK: %[[VAL_6:.*]] = stream.pack %[[VAL_3]], %[[VAL_4]] : tuple 85 | // CHECK: stream.yield %[[VAL_6]], %[[VAL_5]] : tuple, i1 86 | // CHECK: } 87 | // CHECK: return %[[VAL_7:.*]] : !stream.stream> 88 | // CHECK: } 89 | 90 | func.func @combine(%in0: !stream.stream, %in1: !stream.stream) -> (!stream.stream>) { 91 | %res = stream.combine(%in0, %in1) {registers = [0 : i1]}: (!stream.stream, !stream.stream) -> (!stream.stream>) { 92 | ^0(%val0: i32, %val1: i32, %reg: i1): 93 | %0 = stream.pack %val0, %val1 : tuple 94 | stream.yield %0, %reg : tuple, i1 95 | } 96 | return %res : !stream.stream> 97 | } 98 | -------------------------------------------------------------------------------- /test/Dialect/Stream/ops.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --mlir-print-op-generic | stream-opt | FileCheck %s 2 | 3 | module { 4 | func.func @map(%in: !stream.stream) -> !stream.stream { 5 | %res = stream.map(%in) : (!stream.stream) -> !stream.stream { 6 | ^0(%val : i32): 7 | %0 = arith.constant 1 : i32 8 | %r = arith.addi %0, %val : i32 9 | stream.yield %r : i32 10 | } 11 | return %res : !stream.stream 12 | } 13 | 14 | // CHECK: func.func @map(%{{.*}}: !stream.stream) -> !stream.stream { 15 | // CHECK-NEXT: %{{.*}} = stream.map(%{{.*}}) : (!stream.stream) -> !stream.stream { 16 | // CHECK-NEXT: ^{{.*}}(%{{.*}}: i32): 17 | // CHECK-NEXT: %{{.*}} = arith.constant 1 : i32 18 | // CHECK-NEXT: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i32 19 | // CHECK-NEXT: stream.yield %{{.*}} : i32 20 | // CHECK-NEXT: } 21 | // CHECK-NEXT: return %{{.*}} : !stream.stream 22 | // CHECK-NEXT: } 23 | 24 | func.func @filter(%in: !stream.stream) -> !stream.stream { 25 | %res = stream.filter(%in) : (!stream.stream) -> !stream.stream { 26 | ^0(%val : i32): 27 | %cond = arith.constant false 28 | stream.yield %cond : i1 29 | } 30 | return %res : !stream.stream 31 | } 32 | 33 | // CHECK: func.func @filter(%{{.*}}: !stream.stream) -> !stream.stream { 34 | // CHECK-NEXT: %{{.*}} = stream.filter(%{{.*}}) : (!stream.stream) -> !stream.stream { 35 | // CHECK-NEXT: ^{{.*}}(%{{.*}}: i32): 36 | // CHECK-NEXT: %{{.*}} = arith.constant false 37 | // CHECK-NEXT: stream.yield %{{.*}} : i1 38 | // CHECK-NEXT: } 39 | // CHECK-NEXT: return %{{.*}} : !stream.stream 40 | // CHECK-NEXT: } 41 | 42 | func.func @reduce(%in: !stream.stream) -> !stream.stream { 43 | %res = stream.reduce(%in) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 44 | ^0(%acc: i64, %val: i64): 45 | %r = arith.addi %acc, %val : i64 46 | stream.yield %r : i64 47 | } 48 | return %res : !stream.stream 49 | } 50 | 51 | // CHECK: func.func @reduce(%{{.*}}: !stream.stream) -> !stream.stream { 52 | // CHECK-NEXT: %{{.*}} = stream.reduce(%a{{.*}}) {initValue = 0 : i64} : (!stream.stream) -> !stream.stream { 53 | // CHECK-NEXT: ^bb0(%{{.*}}: i64, %{{.*}}: i64): 54 | // CHECK-NEXT: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i64 55 | // CHECK-NEXT: stream.yield %{{.*}} : i64 56 | // CHECK-NEXT: } 57 | // CHECK-NEXT: return %{{.*}} : !stream.stream 58 | // CHECK-NEXT:} 59 | 60 | func.func @tuples(%tuple: tuple) -> tuple { 61 | %a, %b = stream.unpack %tuple : tuple 62 | %res = stream.pack %b, %a : tuple 63 | return %res : tuple 64 | } 65 | 66 | // CHECK: func.func @tuples(%{{.*}}: tuple) -> tuple { 67 | // CHECK-NEXT: %{{.*}}:2 = stream.unpack %{{.*}} : tuple 68 | // CHECK-NEXT: %{{.*}} = stream.pack %{{.*}}#1, %{{.*}}#0 : tuple 69 | // CHECK-NEXT: return %{{.*}} : tuple 70 | // CHECK-NEXT:} 71 | 72 | func.func @split(%in: !stream.stream>) -> (!stream.stream, !stream.stream) { 73 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 74 | ^0(%val: tuple): 75 | %0, %1 = stream.unpack %val : tuple 76 | stream.yield %0, %1 : i32, i32 77 | } 78 | return %res0, %res1 : !stream.stream, !stream.stream 79 | } 80 | 81 | // CHECK: func.func @split(%{{.*}}: !stream.stream>) -> (!stream.stream, !stream.stream) { 82 | // CHECK-NEXT: %{{.*}}:2 = stream.split(%{{.*}}) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 83 | // CHECK-NEXT: ^{{.*}}(%{{.*}}: tuple): 84 | // CHECK-NEXT: %{{.*}}:2 = stream.unpack %{{.*}} : tuple 85 | // CHECK-NEXT: stream.yield %{{.*}}#0, %{{.*}}#1 : i32, i32 86 | // CHECK-NEXT: } 87 | // CHECK-NEXT: return %{{.*}}#0, %{{.*}}#1 : !stream.stream, !stream.stream 88 | // CHECK-NEXT: } 89 | 90 | func.func @combine(%in0: !stream.stream, %in1: !stream.stream) -> (!stream.stream>) { 91 | %res = stream.combine(%in0, %in1) : (!stream.stream, !stream.stream) -> (!stream.stream>) { 92 | ^0(%val0: i32, %val1: i32): 93 | %0 = stream.pack %val0, %val1 : tuple 94 | stream.yield %0 : tuple 95 | } 96 | return %res : !stream.stream> 97 | } 98 | 99 | // CHECK: func.func @combine(%{{.*}}: !stream.stream, %{{.*}}: !stream.stream) -> !stream.stream> { 100 | // CHECK-NEXT: %{{.*}} = stream.combine(%{{.*}}, %{{.*}}) : (!stream.stream, !stream.stream) -> !stream.stream> { 101 | // CHECK-NEXT: ^bb0(%{{.*}}: i32, %{{.*}}: i32): 102 | // CHECK-NEXT: %{{.*}} = stream.pack %{{.*}}, %{{.*}} : tuple 103 | // CHECK-NEXT: stream.yield %{{.*}} : tuple 104 | // CHECK-NEXT: } 105 | // CHECK-NEXT: return %{{.*}} : !stream.stream> 106 | // CHECK-NEXT: } 107 | 108 | func.func @sink(%in: !stream.stream) { 109 | stream.sink %in : !stream.stream 110 | return 111 | } 112 | 113 | // CHECK: func.func @sink(%{{.*}}: !stream.stream) { 114 | // CHECK-NEXT: stream.sink %{{.*}} : !stream.stream 115 | // CHECK-NEXT: return 116 | // CHECK-NEXT: } 117 | } 118 | -------------------------------------------------------------------------------- /test/Dialect/Stream/errors.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --split-input-file --verify-diagnostics 2 | 3 | func.func @map_wrong_arg_types(%in: !stream.stream>) -> (!stream.stream) { 4 | // expected-error @+1 {{expect the block argument #0 to have type 'tuple', got 'i32' instead.}} 5 | %res = stream.map(%in) : (!stream.stream>) -> !stream.stream { 6 | ^0(%val : i32): 7 | %0 = arith.constant 1 : i32 8 | %r = arith.addi %0, %val : i32 9 | stream.yield %r : i32 10 | } 11 | return %res : !stream.stream 12 | } 13 | 14 | // ----- 15 | 16 | func.func @map_wrong_arg_cnt(%in: !stream.stream) -> (!stream.stream) { 17 | // expected-error @+1 {{expect region to have 1 arguments.}} 18 | %res = stream.map(%in) : (!stream.stream) -> !stream.stream { 19 | ^0(%val : i32, %val2 : i64): 20 | stream.yield %val : i32 21 | } 22 | return %res : !stream.stream 23 | } 24 | 25 | // ----- 26 | 27 | func.func @filter_wrong_yield_type(%in: !stream.stream) -> !stream.stream { 28 | %res = stream.filter(%in) : (!stream.stream) -> !stream.stream { 29 | ^0(%val : i32): 30 | // expected-error @+1 {{expect the operand #0 to have type 'i1', got 'i32' instead.}} 31 | stream.yield %val : i32 32 | } 33 | return %res : !stream.stream 34 | } 35 | 36 | // ----- 37 | 38 | func.func @reduce_wrong_arg_types(%in: !stream.stream) -> !stream.stream { 39 | // expected-error @+1 {{expect the block argument #0 to have type 'i8', got 'i32' instead.}} 40 | %res = stream.reduce(%in) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 41 | ^0(%acc: i32, %val: i64): 42 | %0 = arith.constant 1 : i8 43 | stream.yield %0 : i8 44 | } 45 | return %res : !stream.stream 46 | } 47 | 48 | // ----- 49 | 50 | func.func @reduce_wrong_yield_type(%in: !stream.stream) -> !stream.stream { 51 | %res = stream.reduce(%in) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 52 | ^0(%acc: i64, %val: i64): 53 | %0 = arith.constant 1 : i32 54 | // expected-error @+1 {{expect the operand #0 to have type 'i64', got 'i32' instead.}} 55 | stream.yield %0 : i32 56 | } 57 | return %res : !stream.stream 58 | } 59 | 60 | // ----- 61 | 62 | func.func @split_wrong_yield_args(%in: !stream.stream>) -> (!stream.stream, !stream.stream) { 63 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 64 | ^0(%val0: tuple): 65 | %c0 = arith.constant 0 : i32 66 | %c1 = arith.constant 0 : i64 67 | // expected-error @+1 {{expect the operand #1 to have type 'i32', got 'i64' instead.}} 68 | stream.yield %c0, %c1 : i32, i64 69 | } 70 | return %res0, %res1 : !stream.stream, !stream.stream 71 | } 72 | 73 | // ----- 74 | 75 | func.func @split_wrong_arg_types(%in: !stream.stream>) -> (!stream.stream, !stream.stream) { 76 | // expected-error @+1 {{expect the block argument #0 to have type 'tuple', got 'i32' instead.}} 77 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 78 | ^0(%val: i32): 79 | stream.yield %val, %val : i32, i32 80 | } 81 | return %res0, %res1 : !stream.stream, !stream.stream 82 | } 83 | 84 | // ----- 85 | 86 | func.func @split_wrong_arg_num(%in: !stream.stream>) -> (!stream.stream, !stream.stream) { 87 | // expected-error @+1 {{expect region to have 1 arguments.}} 88 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 89 | ^0(%val0: i32, %val1: i32): 90 | stream.yield %val0, %val1 : i32, i32 91 | } 92 | return %res0, %res1 : !stream.stream, !stream.stream 93 | } 94 | 95 | // ----- 96 | 97 | func.func @split_wrong_yield_cnt(%in: !stream.stream>) -> (!stream.stream, !stream.stream) { 98 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 99 | ^0(%val0: tuple): 100 | %c = arith.constant 0 : i32 101 | // expected-error @+1 {{expect 2 operands, got 1}} 102 | stream.yield %c : i32 103 | } 104 | return %res0, %res1 : !stream.stream, !stream.stream 105 | } 106 | 107 | // ----- 108 | 109 | func.func @split_wrong_yield_args(%in: !stream.stream>) -> (!stream.stream, !stream.stream) { 110 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 111 | ^0(%val0: tuple): 112 | %c0 = arith.constant 0 : i32 113 | %c1 = arith.constant 0 : i64 114 | // expected-error @+1 {{expect the operand #1 to have type 'i32', got 'i64' instead.}} 115 | stream.yield %c0, %c1 : i32, i64 116 | } 117 | return %res0, %res1 : !stream.stream, !stream.stream 118 | } 119 | 120 | // ----- 121 | 122 | func.func @combine_wrong_input_types(%in0: !stream.stream, %in1: !stream.stream) -> (!stream.stream>) { 123 | // expected-error @+1 {{expect the block argument #1 to have type 'i32', got 'i64' instead.}} 124 | %res = stream.combine(%in0, %in1) : (!stream.stream, !stream.stream) -> (!stream.stream>) { 125 | ^0(%val0: i32, %val1: i64): 126 | %0 = stream.pack %val0, %val0 : tuple 127 | stream.yield %0 : tuple 128 | } 129 | return %res : !stream.stream> 130 | } 131 | 132 | // ----- 133 | 134 | func.func @map_not_isolated(%in: !stream.stream) -> (!stream.stream) { 135 | %c = arith.constant 42 : i32 136 | // expected-note @+1 {{required by region isolation constrain}} 137 | %res = stream.map(%in) : (!stream.stream) -> !stream.stream { 138 | ^bb0(%val : i32): 139 | // expected-error @+1 {{op using value defined outside the region}} 140 | %out = arith.addi %val, %c : i32 141 | stream.yield %out : i32 142 | } 143 | return %res : !stream.stream 144 | } 145 | -------------------------------------------------------------------------------- /lib/Transform/CustomBufferInsertion.cpp: -------------------------------------------------------------------------------- 1 | //===- CustomBufferInsertion.cpp ------------------------------------------===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #include "circt-stream/Transform/CustomBufferInsertion.h" 10 | #include "circt-stream/Transform/Passes.h" 11 | #include "circt/Dialect/Handshake/HandshakeDialect.h" 12 | #include "circt/Dialect/Handshake/HandshakeOps.h" 13 | #include "circt/Dialect/Handshake/HandshakePasses.h" 14 | #include "mlir/Pass/PassManager.h" 15 | #include "mlir/Transforms/DialectConversion.h" 16 | #include "llvm/ADT/SetOperations.h" 17 | 18 | using namespace mlir; 19 | using namespace circt; 20 | using namespace circt::handshake; 21 | using namespace circt_stream; 22 | 23 | namespace circt_stream { 24 | #define GEN_PASS_DEF_CUSTOMBUFFERINSERTION 25 | #include "circt-stream/Transform/Passes.h.inc" 26 | } // namespace circt_stream 27 | 28 | static bool doesLoop(Operation *op) { 29 | SmallVector stack = llvm::to_vector(op->getUsers()); 30 | DenseSet visited; 31 | 32 | while (!stack.empty()) { 33 | Operation *curr = stack.pop_back_val(); 34 | if (visited.contains(curr)) 35 | continue; 36 | visited.insert(curr); 37 | 38 | if (curr == op) 39 | return true; 40 | 41 | llvm::copy(curr->getUsers(), std::back_inserter(stack)); 42 | } 43 | return false; 44 | } 45 | 46 | template 47 | static void traverse(Operation *op, DenseSet &res, Func f) { 48 | SmallVector stack = {op}; 49 | 50 | while (!stack.empty()) { 51 | auto *curr = stack.pop_back_val(); 52 | if (res.contains(curr)) 53 | continue; 54 | res.insert(curr); 55 | 56 | f(curr, stack); 57 | } 58 | } 59 | 60 | static Value insertBuffer(Location loc, Value operand, OpBuilder &builder, 61 | unsigned numSlots, BufferTypeEnum bufferType) { 62 | auto ip = builder.saveInsertionPoint(); 63 | builder.setInsertionPointAfterValue(operand); 64 | auto bufferOp = builder.create( 65 | loc, operand.getType(), numSlots, operand, bufferType); 66 | operand.replaceUsesWithIf( 67 | bufferOp, function_ref([](OpOperand &operand) -> bool { 68 | return !isa(operand.getOwner()); 69 | })); 70 | builder.restoreInsertionPoint(ip); 71 | return bufferOp; 72 | } 73 | 74 | static LogicalResult findAllLoopElements(Operation *op, 75 | DenseSet &elements) { 76 | 77 | DenseSet preds, succs; 78 | 79 | traverse(op, preds, 80 | [&](Operation *curr, SmallVectorImpl &stack) { 81 | for (auto operand : curr->getOperands()) { 82 | if (operand.isa()) 83 | continue; 84 | stack.push_back(operand.getDefiningOp()); 85 | } 86 | }); 87 | 88 | traverse(op, succs, 89 | [&](Operation *curr, SmallVectorImpl &stack) { 90 | for (auto *user : curr->getUsers()) { 91 | stack.push_back(user); 92 | } 93 | }); 94 | 95 | llvm::set_intersect(preds, succs); 96 | for (auto *o : preds) { 97 | for (auto res : o->getResults()) { 98 | assert(res.hasOneUse()); 99 | bool valInCycle = llvm::any_of(res.getUsers(), [&](Operation *user) { 100 | return preds.contains(user); 101 | }); 102 | if (valInCycle) 103 | elements.insert(res); 104 | } 105 | } 106 | 107 | return success(); 108 | } 109 | 110 | static LogicalResult findCycleElements(Region &r, 111 | DenseSet &cycleElements) { 112 | SmallVector loopStarts; 113 | Block *b = &r.front(); 114 | llvm::copy_if(b->getOps(), std::back_inserter(loopStarts), 115 | doesLoop); 116 | 117 | for (auto op : loopStarts) 118 | if (failed(findAllLoopElements(op, cycleElements))) 119 | return failure(); 120 | 121 | return success(); 122 | } 123 | 124 | static bool isUnbufferedChannel(Value &val) { 125 | assert(val.hasOneUse()); 126 | Operation *definingOp = val.getDefiningOp(); 127 | Operation *usingOp = *val.user_begin(); 128 | return !isa_and_nonnull(definingOp) && !isa(usingOp); 129 | } 130 | 131 | static LogicalResult customRegionBuffer(Region &r, unsigned fifoBufferSize) { 132 | DenseSet cycleElements; 133 | if (failed(findCycleElements(r, cycleElements))) 134 | return failure(); 135 | 136 | OpBuilder builder(r.getParentOp()); 137 | 138 | // Insert buffers for block arguments as well. 139 | for (Value arg : r.getArguments()) { 140 | if (fifoBufferSize > 0) 141 | arg = insertBuffer(arg.getLoc(), arg, builder, fifoBufferSize, 142 | BufferTypeEnum::fifo); 143 | insertBuffer(arg.getLoc(), arg, builder, 1, BufferTypeEnum::seq); 144 | } 145 | 146 | for (auto &defOp : llvm::make_early_inc_range(r.getOps())) { 147 | for (Value res : defOp.getResults()) { 148 | if (cycleElements.contains(res) || !isUnbufferedChannel(res)) 149 | continue; 150 | if (fifoBufferSize > 0) 151 | res = insertBuffer(res.getLoc(), res, builder, fifoBufferSize, 152 | BufferTypeEnum::fifo); 153 | insertBuffer(res.getLoc(), res, builder, 1, BufferTypeEnum::seq); 154 | } 155 | } 156 | 157 | return success(); 158 | } 159 | 160 | namespace { 161 | class CustomBufferInsertionPass 162 | : public circt_stream::impl::CustomBufferInsertionBase< 163 | CustomBufferInsertionPass> { 164 | public: 165 | using CustomBufferInsertionBase::CustomBufferInsertionBase; 166 | void runOnOperation() override { 167 | // Assumption: only very small cycles and no memory operations 168 | auto f = getOperation(); 169 | if (f.isExternal()) 170 | return; 171 | 172 | if (failed(customRegionBuffer(f.getBody(), fifoBufferSize))) 173 | signalPassFailure(); 174 | } 175 | }; 176 | } // namespace 177 | -------------------------------------------------------------------------------- /test/Transform/custom-buffer-insertion.mlir: -------------------------------------------------------------------------------- 1 | // RUN: stream-opt %s --custom-buffer-insertion | FileCheck %s 2 | 3 | // CHECK-LABEL: handshake.func private @stream_reduce( 4 | // CHECK-SAME: %[[VAL_0:.*]]: tuple, 5 | // CHECK-SAME: %[[VAL_1:.*]]: none, ...) -> (tuple, none) 6 | // CHECK: %[[VAL_2:.*]] = buffer [10] fifo %[[VAL_1]] : none 7 | // CHECK: %[[VAL_3:.*]] = buffer [1] seq %[[VAL_2]] : none 8 | // CHECK: %[[VAL_4:.*]] = buffer [10] fifo %[[VAL_0]] : tuple 9 | // CHECK: %[[VAL_5:.*]] = buffer [1] seq %[[VAL_4]] : tuple 10 | // CHECK: %[[VAL_6:.*]]:2 = unpack %[[VAL_5]] : tuple 11 | // CHECK: %[[VAL_7:.*]] = buffer [10] fifo %[[VAL_6]]#1 : i1 12 | // CHECK: %[[VAL_8:.*]] = buffer [1] seq %[[VAL_7]] : i1 13 | // CHECK: %[[VAL_9:.*]] = buffer [10] fifo %[[VAL_6]]#0 : i64 14 | // CHECK: %[[VAL_10:.*]] = buffer [1] seq %[[VAL_9]] : i64 15 | // CHECK: %[[VAL_11:.*]]:4 = fork [4] %[[VAL_8]] : i1 16 | // CHECK: %[[VAL_12:.*]] = buffer [10] fifo %[[VAL_11]]#3 : i1 17 | // CHECK: %[[VAL_13:.*]] = buffer [1] seq %[[VAL_12]] : i1 18 | // CHECK: %[[VAL_14:.*]] = buffer [10] fifo %[[VAL_11]]#2 : i1 19 | // CHECK: %[[VAL_15:.*]] = buffer [1] seq %[[VAL_14]] : i1 20 | // CHECK: %[[VAL_16:.*]] = buffer [10] fifo %[[VAL_11]]#1 : i1 21 | // CHECK: %[[VAL_17:.*]] = buffer [1] seq %[[VAL_16]] : i1 22 | // CHECK: %[[VAL_18:.*]] = buffer [10] fifo %[[VAL_11]]#0 : i1 23 | // CHECK: %[[VAL_19:.*]] = buffer [1] seq %[[VAL_18]] : i1 24 | // CHECK: %[[VAL_20:.*]] = buffer [1] seq %[[VAL_21:.*]] {initValues = [0]} : i64 25 | // CHECK: %[[VAL_22:.*]], %[[VAL_23:.*]] = cond_br %[[VAL_13]], %[[VAL_20]] : i64 26 | // CHECK: %[[VAL_24:.*]] = buffer [10] fifo %[[VAL_22]] : i64 27 | // CHECK: %[[VAL_25:.*]] = buffer [1] seq %[[VAL_24]] : i64 28 | // CHECK: %[[VAL_26:.*]]:2 = fork [2] %[[VAL_25]] : i64 29 | // CHECK: %[[VAL_27:.*]] = buffer [10] fifo %[[VAL_26]]#1 : i64 30 | // CHECK: %[[VAL_28:.*]] = buffer [1] seq %[[VAL_27]] : i64 31 | // CHECK: %[[VAL_29:.*]] = buffer [10] fifo %[[VAL_26]]#0 : i64 32 | // CHECK: %[[VAL_30:.*]] = buffer [1] seq %[[VAL_29]] : i64 33 | // CHECK: %[[VAL_31:.*]], %[[VAL_32:.*]] = cond_br %[[VAL_17]], %[[VAL_15]] : i1 34 | // CHECK: %[[VAL_33:.*]] = buffer [10] fifo %[[VAL_32]] : i1 35 | // CHECK: %[[VAL_34:.*]] = buffer [1] seq %[[VAL_33]] : i1 36 | // CHECK: %[[VAL_35:.*]] = buffer [10] fifo %[[VAL_31]] : i1 37 | // CHECK: %[[VAL_36:.*]] = buffer [1] seq %[[VAL_35]] : i1 38 | // CHECK: sink %[[VAL_34]] : i1 39 | // CHECK: %[[VAL_37:.*]], %[[VAL_38:.*]] = cond_br %[[VAL_19]], %[[VAL_3]] : none 40 | // CHECK: %[[VAL_39:.*]] = buffer [10] fifo %[[VAL_38]] : none 41 | // CHECK: %[[VAL_40:.*]] = buffer [1] seq %[[VAL_39]] : none 42 | // CHECK: %[[VAL_41:.*]] = buffer [10] fifo %[[VAL_37]] : none 43 | // CHECK: %[[VAL_42:.*]] = buffer [1] seq %[[VAL_41]] : none 44 | // CHECK: sink %[[VAL_40]] : none 45 | // CHECK: %[[VAL_43:.*]]:3 = fork [3] %[[VAL_42]] : none 46 | // CHECK: %[[VAL_44:.*]] = buffer [10] fifo %[[VAL_43]]#2 : none 47 | // CHECK: %[[VAL_45:.*]] = buffer [1] seq %[[VAL_44]] : none 48 | // CHECK: %[[VAL_46:.*]] = buffer [10] fifo %[[VAL_43]]#1 : none 49 | // CHECK: %[[VAL_47:.*]] = buffer [1] seq %[[VAL_46]] : none 50 | // CHECK: %[[VAL_48:.*]] = buffer [10] fifo %[[VAL_43]]#0 : none 51 | // CHECK: %[[VAL_49:.*]] = buffer [1] seq %[[VAL_48]] : none 52 | // CHECK: %[[VAL_50:.*]]:2 = fork [2] %[[VAL_23]] : i64 53 | // CHECK: %[[VAL_51:.*]]:2 = fork [2] %[[VAL_10]] : i64 54 | // CHECK: %[[VAL_52:.*]] = buffer [10] fifo %[[VAL_51]]#1 : i64 55 | // CHECK: %[[VAL_53:.*]] = buffer [1] seq %[[VAL_52]] : i64 56 | // CHECK: %[[VAL_54:.*]] = buffer [10] fifo %[[VAL_51]]#0 : i64 57 | // CHECK: %[[VAL_55:.*]] = buffer [1] seq %[[VAL_54]] : i64 58 | // CHECK: %[[VAL_56:.*]] = arith.cmpi slt, %[[VAL_50]]#0, %[[VAL_55]] : i64 59 | // CHECK: %[[VAL_21]] = select %[[VAL_56]], %[[VAL_53]], %[[VAL_50]]#1 : i64 60 | // CHECK: %[[VAL_57:.*]] = constant %[[VAL_45]] {value = false} : i1 61 | // CHECK: %[[VAL_58:.*]] = buffer [10] fifo %[[VAL_57]] : i1 62 | // CHECK: %[[VAL_59:.*]] = buffer [1] seq %[[VAL_58]] : i1 63 | // CHECK: %[[VAL_60:.*]] = pack %[[VAL_28]], %[[VAL_59]] : tuple 64 | // CHECK: %[[VAL_61:.*]] = buffer [10] fifo %[[VAL_60]] : tuple 65 | // CHECK: %[[VAL_62:.*]] = buffer [1] seq %[[VAL_61]] : tuple 66 | // CHECK: %[[VAL_63:.*]] = pack %[[VAL_30]], %[[VAL_36]] : tuple 67 | // CHECK: %[[VAL_64:.*]] = buffer [10] fifo %[[VAL_63]] : tuple 68 | // CHECK: %[[VAL_65:.*]] = buffer [1] seq %[[VAL_64]] : tuple 69 | // CHECK: %[[VAL_66:.*]]:2 = fork [2] %[[VAL_65]] : tuple 70 | // CHECK: %[[VAL_67:.*]] = buffer [10] fifo %[[VAL_66]]#1 : tuple 71 | // CHECK: %[[VAL_68:.*]] = buffer [1] seq %[[VAL_67]] : tuple 72 | // CHECK: %[[VAL_69:.*]] = buffer [10] fifo %[[VAL_66]]#0 : tuple 73 | // CHECK: %[[VAL_70:.*]] = buffer [1] seq %[[VAL_69]] : tuple 74 | // CHECK: %[[VAL_71:.*]] = constant %[[VAL_47]] {value = false} : i1 75 | // CHECK: %[[VAL_72:.*]] = buffer [2] seq %[[VAL_71]] {initValues = [1, 0]} : i1 76 | // CHECK: %[[VAL_73:.*]]:2 = fork [2] %[[VAL_72]] : i1 77 | // CHECK: %[[VAL_74:.*]] = buffer [10] fifo %[[VAL_73]]#1 : i1 78 | // CHECK: %[[VAL_75:.*]] = buffer [1] seq %[[VAL_74]] : i1 79 | // CHECK: %[[VAL_76:.*]] = buffer [10] fifo %[[VAL_73]]#0 : i1 80 | // CHECK: %[[VAL_77:.*]] = buffer [1] seq %[[VAL_76]] : i1 81 | // CHECK: %[[VAL_78:.*]] = mux %[[VAL_75]] {{\[}}%[[VAL_62]], %[[VAL_68]]] : i1, tuple 82 | // CHECK: %[[VAL_79:.*]] = buffer [10] fifo %[[VAL_78]] : tuple 83 | // CHECK: %[[VAL_80:.*]] = buffer [1] seq %[[VAL_79]] : tuple 84 | // CHECK: %[[VAL_81:.*]] = join %[[VAL_70]] : tuple 85 | // CHECK: %[[VAL_82:.*]] = buffer [10] fifo %[[VAL_81]] : none 86 | // CHECK: %[[VAL_83:.*]] = buffer [1] seq %[[VAL_82]] : none 87 | // CHECK: %[[VAL_84:.*]] = mux %[[VAL_77]] {{\[}}%[[VAL_49]], %[[VAL_83]]] : i1, none 88 | // CHECK: %[[VAL_85:.*]] = buffer [10] fifo %[[VAL_84]] : none 89 | // CHECK: %[[VAL_86:.*]] = buffer [1] seq %[[VAL_85]] : none 90 | // CHECK: return %[[VAL_80]], %[[VAL_86]] : tuple, none 91 | // CHECK: } 92 | 93 | handshake.func private @stream_reduce(%arg0: tuple, %arg1: none, ...) -> (tuple, none) { 94 | %0:2 = unpack %arg0 : tuple 95 | %1:4 = fork [4] %0#1 : i1 96 | %2 = buffer [1] seq %8 {initValues = [0]} : i64 97 | %trueResult, %falseResult = cond_br %1#3, %2 : i64 98 | %3:2 = fork [2] %trueResult : i64 99 | %trueResult_0, %falseResult_1 = cond_br %1#1, %1#2 : i1 100 | sink %falseResult_1 : i1 101 | %trueResult_2, %falseResult_3 = cond_br %1#0, %arg1 : none 102 | sink %falseResult_3 : none 103 | %4:3 = fork [3] %trueResult_2 : none 104 | %5:2 = fork [2] %falseResult : i64 105 | %6:2 = fork [2] %0#0 : i64 106 | %7 = arith.cmpi slt, %5#0, %6#0 : i64 107 | %8 = select %7, %6#1, %5#1 : i64 108 | %9 = constant %4#2 {value = false} : i1 109 | %10 = pack %3#1, %9 : tuple 110 | %11 = pack %3#0, %trueResult_0 : tuple 111 | %12:2 = fork [2] %11 : tuple 112 | %13 = constant %4#1 {value = false} : i1 113 | %14 = buffer [2] seq %13 {initValues = [1, 0]} : i1 114 | %15:2 = fork [2] %14 : i1 115 | %16 = mux %15#1 [%10, %12#1] : i1, tuple 116 | %17 = join %12#0 : tuple 117 | %18 = mux %15#0 [%4#0, %17] : i1, none 118 | return %16, %18 : tuple, none 119 | } 120 | -------------------------------------------------------------------------------- /include/circt-stream/Dialect/Stream/StreamOps.td: -------------------------------------------------------------------------------- 1 | //===- StreamOps.td - Stream dialect ops -------------------*- tablegen -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #ifndef CIRCT_STREAM_DIALECT_STREAM_OPS_TD 10 | #define CIRCT_STREAM_DIALECT_STREAM_OPS_TD 11 | 12 | include "circt-stream/Dialect/Stream/StreamDialect.td" 13 | include "circt-stream/Dialect/Stream/StreamTypes.td" 14 | include "mlir/Interfaces/ControlFlowInterfaces.td" 15 | include "mlir/Interfaces/InferTypeOpInterface.td" 16 | include "mlir/Interfaces/SideEffectInterfaces.td" 17 | include "mlir/IR/BuiltinTypes.td" 18 | 19 | // TODO Refine this to ensure that each element holds data and a type 20 | def RegisterAttr : OptionalAttr; 21 | 22 | def MapOp : Stream_Op<"map", [IsolatedFromAbove]> { 23 | let summary = "applies the region on each element"; 24 | let description = [{ 25 | `stream.map` applies the provided region on each element of the input 26 | stream. 27 | The result will be emitted on the output stream. 28 | 29 | Example: 30 | ```mlir 31 | %res = stream.map(%in) : (!stream.stream) -> !stream.stream { 32 | ^0(%val : i32): 33 | %0 = arith.constant 1 : i32 34 | %r = arith.addi %0, %val : i32 35 | stream.yield %r : i32 36 | } 37 | ``` 38 | }]; 39 | 40 | let arguments = (ins StreamType:$input, RegisterAttr:$registers); 41 | let results = (outs StreamType:$res); 42 | let regions = (region AnyRegion:$region); 43 | 44 | let assemblyFormat = [{ 45 | `(` $input `)` attr-dict `:` functional-type($input, $res) $region 46 | }]; 47 | 48 | let hasRegionVerifier = 1; 49 | } 50 | 51 | def FilterOp : Stream_Op<"filter", [IsolatedFromAbove]> { 52 | let summary = "filters a stream with the provided predicate"; 53 | let description = [{ 54 | `stream.filter` applies the provided region on each element of the input 55 | stream. 56 | If the result is true/1, then the input element is forwarded to the output, 57 | otherwise it's dropped. 58 | 59 | Example: 60 | ```mlir 61 | %out = stream.filter(%in) : (!stream.stream) -> !stream.stream { 62 | ^bb0(%val: i32): 63 | %c0_i32 = arith.constant 0 : i32 64 | %0 = arith.cmpi sgt, %val, %c0_i32 : i32 65 | stream.yield %0 : i1 66 | } 67 | ``` 68 | }]; 69 | 70 | let arguments = (ins StreamType:$input, RegisterAttr:$registers); 71 | let results = (outs StreamType:$res); 72 | let regions = (region AnyRegion:$region); 73 | 74 | let assemblyFormat = [{ 75 | `(` $input `)` attr-dict `:` functional-type($input, $res) $region 76 | }]; 77 | 78 | let hasRegionVerifier = 1; 79 | } 80 | 81 | def ReduceOp : Stream_Op<"reduce", [IsolatedFromAbove]> { 82 | let summary = "reduces the stream with the provided region"; 83 | let description = [{ 84 | `stream.reduce` folds the stream to a single value by applying the provided 85 | region on each element. The result of one such application is provided to 86 | the next one as the first parameter. 87 | 88 | Example: 89 | ```mlir 90 | %res = stream.reduce(%in) {initValue = 0 : i64}: (!stream.stream) -> !stream.stream { 91 | ^0(%acc: i64, %val: i64): 92 | %r = arith.addi %acc, %val : i64 93 | stream.yield %r : i64 94 | } 95 | ``` 96 | }]; 97 | 98 | let arguments = ( 99 | ins StreamType:$input, 100 | I64Attr:$initValue, 101 | RegisterAttr:$registers); 102 | 103 | let results = (outs StreamType:$result); 104 | let regions = (region AnyRegion:$region); 105 | 106 | let assemblyFormat = [{ 107 | `(` $input `)` attr-dict `:` functional-type(operands, $result) $region 108 | }]; 109 | 110 | let hasRegionVerifier = 1; 111 | } 112 | 113 | def UnpackOp : Stream_Op<"unpack", [ 114 | Pure, 115 | TypesMatchWith<"result types match element types of 'tuple'", 116 | "input", "results", 117 | "$_self.cast().getTypes()">]> { 118 | 119 | let summary = "unpacks a tuple"; 120 | let description = [{ 121 | The `unpack` operation assignes each value of a tuple to a separate 122 | value for further processing. The number of results corresponds 123 | to the number of tuple elements. 124 | 125 | Example: 126 | ```mlir 127 | %a, %b = stream.unpack %tuple {attributes} : tuple 128 | ``` 129 | }]; 130 | 131 | let arguments = (ins Builtin_Tuple:$input); 132 | let results = (outs Variadic:$results); 133 | 134 | let hasCustomAssemblyFormat = 1; 135 | 136 | let hasCanonicalizeMethod = 1; 137 | } 138 | 139 | def PackOp : Stream_Op<"pack", [ 140 | Pure, 141 | TypesMatchWith<"input types match element types of 'tuple'", 142 | "result", "inputs", 143 | "$_self.cast().getTypes()"> 144 | ]> { 145 | let summary = "packs a tuple"; 146 | let description = [{ 147 | The `pack` operation constructs a tuple from separate values. 148 | The number of operands corresponds to the number of tuple elements. 149 | 150 | Example: 151 | ```mlir 152 | %tuple = stream.pack %a, %b {attributes} : tuple 153 | ``` 154 | }]; 155 | 156 | let arguments = (ins Variadic:$inputs); 157 | let results = (outs Builtin_Tuple:$result); 158 | 159 | let hasCustomAssemblyFormat = 1; 160 | 161 | let hasCanonicalizeMethod = 1; 162 | } 163 | 164 | def YieldOp : Stream_Op<"yield", [ 165 | ReturnLike, Terminator, 166 | ParentOneOf<["MapOp, FilterOp, ReduceOp, SplitOp, CombineOp"]> 167 | ]> { 168 | let summary = "stream yield and termination operation"; 169 | let description = [{ 170 | "stream.yield" yields an SSA value from the steam dialect op region and 171 | terminates the regions. The semantics of how the values are yielded is 172 | defined by the parent operation. 173 | }]; 174 | 175 | let arguments = (ins Variadic:$results); 176 | let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>]; 177 | 178 | let assemblyFormat = 179 | [{ attr-dict ($results^ `:` type($results))? }]; 180 | } 181 | 182 | def SplitOp : Stream_Op<"split", [IsolatedFromAbove]> { 183 | let summary = "for each input produces outputs for each output stream."; 184 | let description = [{ 185 | `stream.split` takes one input stream and produces multiple output streams. 186 | For each input element, each output stream expects an element. The exact 187 | splitting is implemented in a region. 188 | 189 | Example: 190 | ```mlir 191 | %res0, %res1 = stream.split(%in) : (!stream.stream>) -> (!stream.stream, !stream.stream) { 192 | ^0(%val: tuple): 193 | %0, %1 = stream.unpack %val : tuple 194 | stream.yield %0, %1 : i32, i32 195 | } 196 | ``` 197 | }]; 198 | 199 | let arguments = (ins StreamType:$input, RegisterAttr:$registers); 200 | let results = (outs Variadic:$results); 201 | let regions = (region AnyRegion:$region); 202 | 203 | let assemblyFormat = [{ 204 | `(` $input `)` attr-dict `:` functional-type($input, $results) $region 205 | }]; 206 | 207 | let hasRegionVerifier = 1; 208 | } 209 | 210 | def CombineOp : Stream_Op<"combine", [IsolatedFromAbove]> { 211 | let summary = "combines elements from all incomming stream to one output element"; 212 | let description = [{ 213 | `stream.combine` takes a variable number of input stream and produces one output stream. 214 | When all input streams have an element ready, the provided region is executed to 215 | produce an output element. 216 | 217 | Example: 218 | ```mlir 219 | %res = stream.combine(%in0, %in1) : (!stream.stream, !stream.stream) -> (!stream.stream>) { 220 | ^0(%val0: i32, %val1: i32): 221 | %0 = stream.pack %val0, %val1 : tuple 222 | stream.yield %0 : tuple 223 | } 224 | ``` 225 | }]; 226 | 227 | let arguments = (ins Variadic:$inputs, RegisterAttr:$registers); 228 | let results = (outs StreamType:$result); 229 | let regions = (region AnyRegion:$region); 230 | 231 | let assemblyFormat = [{ 232 | `(` $inputs `)` attr-dict `:` functional-type($inputs, $result) $region 233 | }]; 234 | 235 | let hasRegionVerifier = 1; 236 | } 237 | 238 | def SinkOp : Stream_Op<"sink", [ 239 | Pure 240 | ]> { 241 | let summary = "consumes elements of the input stream"; 242 | let description = [{ 243 | "stream.sink" consumes all elements of the input stream while not having any output. 244 | 245 | Example: 246 | ```mlir 247 | stream.sink %in : !stream.stream 248 | ``` 249 | }]; 250 | 251 | let arguments = (ins StreamType:$input); 252 | 253 | let assemblyFormat = [{ 254 | $input attr-dict `:` qualified(type($input)) 255 | }]; 256 | } 257 | 258 | #endif // CIRCT_STREAM_DIALECT_STREAM_OPS_TD 259 | -------------------------------------------------------------------------------- /integration_test/Dialect/Stream/helper.py: -------------------------------------------------------------------------------- 1 | import cocotb 2 | import cocotb.clock 3 | from cocotb.triggers import RisingEdge, ReadOnly 4 | import re 5 | 6 | 7 | #TODO reuse parts of CIRCT's cocotb helper 8 | class HandshakePort: 9 | """ 10 | Helper class that encapsulates a handshake port from the DUT. 11 | """ 12 | 13 | def __init__(self, dut, rdy, val): 14 | self.dut = dut 15 | self.ready = rdy 16 | self.valid = val 17 | 18 | def isReady(self): 19 | return self.ready.value.is_resolvable and self.ready.value == 1 20 | 21 | def setReady(self, v): 22 | self.ready.value = v 23 | 24 | def isValid(self): 25 | return self.valid.value.is_resolvable and self.valid.value == 1 26 | 27 | def isCtrl(self): 28 | return True 29 | 30 | def setValid(self, v): 31 | self.valid.value = v 32 | 33 | async def waitUntilReady(self): 34 | while (not self.isReady()): 35 | await RisingEdge(self.dut.clock) 36 | 37 | async def waitUntilValid(self): 38 | while (not self.isValid()): 39 | await RisingEdge(self.dut.clock) 40 | 41 | async def awaitHandshake(self): 42 | # Make sure that changes to ready are propagated before it is checked. 43 | await ReadOnly() 44 | directSend = self.isReady() 45 | await self.waitUntilReady() 46 | 47 | if (directSend): 48 | # If it was initially ready, the handshake happens in the current cycle. 49 | # Thus the invalidation has to wait until the next cycle 50 | await RisingEdge(self.dut.clock) 51 | 52 | self.setValid(0) 53 | 54 | async def send(self, val=None): 55 | self.setValid(1) 56 | await self.awaitHandshake() 57 | 58 | async def awaitNOutputs(self, n): 59 | assert (self.isReady()) 60 | for _ in range(n): 61 | await self.waitUntilValid() 62 | await RisingEdge(self.dut.clock) 63 | 64 | 65 | class HandshakeDataPort(HandshakePort): 66 | """ 67 | A handshaked port with a data field. 68 | """ 69 | 70 | def __init__(self, dut, rdy, val, data): 71 | super().__init__(dut, rdy, val) 72 | self.data = data 73 | 74 | def isCtrl(self): 75 | return False 76 | 77 | async def send(self, val): 78 | self.data.value = val 79 | await super().send() 80 | 81 | async def checkOutputs(self, results): 82 | assert (self.isReady()) 83 | for res in results: 84 | await self.waitUntilValid() 85 | assert (self.data.value == res) 86 | await RisingEdge(self.dut.clock) 87 | 88 | async def collectNOutputs(self, n): 89 | assert (self.isReady()) 90 | res = [] 91 | for _ in range(n): 92 | await self.waitUntilValid() 93 | res.append(self.data.value.integer) 94 | await RisingEdge(self.dut.clock) 95 | return res 96 | 97 | async def collectUntil(self, checkFunc): 98 | assert (self.isReady()) 99 | res = [] 100 | while True: 101 | await self.waitUntilValid() 102 | val = self.data.value.integer 103 | res.append(val) 104 | await RisingEdge(self.dut.clock) 105 | if (checkFunc(res, val)): 106 | break 107 | return res 108 | 109 | 110 | class HandshakeTuplePort(HandshakePort): 111 | """ 112 | A handshaked port that sends a tuple. 113 | """ 114 | 115 | def __init__(self, dut, rdy, val, fields): 116 | super().__init__(dut, rdy, val) 117 | self.fields = fields 118 | 119 | def isCtrl(self): 120 | return False 121 | 122 | def _assignTupleValue(self, val, curr): 123 | assert (len(list(val)) == len(curr)) 124 | for (f, v) in zip(curr, list(val)): 125 | if (isinstance(f, list)): 126 | assert isinstance(v, tuple) 127 | self._assignTupleValue(v, f) 128 | else: 129 | assert not isinstance(v, tuple) 130 | f.value = v 131 | 132 | async def send(self, val): 133 | self._assignTupleValue(val, self.fields) 134 | 135 | await super().send() 136 | 137 | def _collectValRec(self, curr): 138 | if not isinstance(curr, list): 139 | return curr.value.integer 140 | return tuple([self._collectValRec(f) for f in curr]) 141 | 142 | async def checkOutputs(self, results): 143 | assert (self.isReady()) 144 | for res in results: 145 | await self.waitUntilValid() 146 | val = self._collectValRec(self.fields) 147 | assert (res == val) 148 | 149 | await RisingEdge(self.dut.clock) 150 | 151 | async def collectNOutputs(self, n): 152 | assert (self.isReady()) 153 | res = [] 154 | for _ in range(n): 155 | await self.waitUntilValid() 156 | t = self._collectValRec(self.fields) 157 | res.append(t) 158 | await RisingEdge(self.dut.clock) 159 | return res 160 | 161 | async def collectUntil(self, checkFunc): 162 | assert (self.isReady()) 163 | res = [] 164 | while True: 165 | await self.waitUntilValid() 166 | t = tuple([f.value.integer for f in self.fields]) 167 | res.append(t) 168 | await RisingEdge(self.dut.clock) 169 | if (checkFunc(res, t)): 170 | break 171 | return res 172 | 173 | 174 | def buildTupleStructure(dut, tupleFields, prefix): 175 | """ 176 | Helper that builds a neasted list structure that represents the nester tuple 177 | 178 | structure of the inputs. 179 | """ 180 | size = 0 181 | while True: 182 | r = re.compile(f"{prefix}_field{size}") 183 | found = False 184 | for f in tupleFields: 185 | if r.match(f): 186 | found = True 187 | break 188 | 189 | if not found: 190 | break 191 | size += 1 192 | 193 | res = [] 194 | for i in range(size): 195 | fName = f"{prefix}_field{i}" 196 | if fName in tupleFields: 197 | res.append(getattr(dut, fName)) 198 | continue 199 | 200 | res.append(buildTupleStructure(dut, tupleFields, fName)) 201 | 202 | return res 203 | 204 | 205 | def _findPort(dut, name): 206 | """ 207 | Checks if dut has a port of the provided name. Either throws an exception or 208 | returns a HandshakePort that encapsulates the dut's interface. 209 | """ 210 | readyName = f"{name}_ready" 211 | validName = f"{name}_valid" 212 | dataName = f"{name}_data" 213 | if (not hasattr(dut, readyName) or not hasattr(dut, validName)): 214 | raise Exception(f"dut does not have a port named {name}") 215 | 216 | ready = getattr(dut, readyName) 217 | valid = getattr(dut, validName) 218 | data = getattr(dut, dataName, None) 219 | 220 | # Needed, as it otherwise would try to resolve the value 221 | hasData = not isinstance(data, type(None)) 222 | if hasData: 223 | return HandshakeDataPort(dut, ready, valid, data) 224 | 225 | isCtrl = not hasattr(dut, f"{name}_data_field0") 226 | 227 | r = re.compile(f"^{name}_data_field") 228 | tupleFields = [f for f in dir(dut) if r.match(f)] 229 | isCtrl = not any(tupleFields) 230 | 231 | if (isCtrl): 232 | return HandshakePort(dut, ready, valid) 233 | 234 | fields = buildTupleStructure(dut, tupleFields, f"{name}_data") 235 | 236 | return HandshakeTuplePort(dut, ready, valid, fields) 237 | 238 | 239 | def getPorts(dut, inNames, outNames): 240 | """ 241 | Helper function to produce in and out ports for the provided dut. 242 | """ 243 | ins = [_findPort(dut, name) for name in inNames] 244 | outs = [_findPort(dut, name) for name in outNames] 245 | return ins, outs 246 | 247 | 248 | def getNames(dut, prefix): 249 | names = [] 250 | 251 | i = 0 252 | while hasattr(dut, f"{prefix}{i}_ready"): 253 | names.append(f"{prefix}{i}") 254 | i += 1 255 | 256 | return names 257 | 258 | 259 | def getInNames(dut): 260 | return getNames(dut, "in") 261 | 262 | 263 | def getOutNames(dut): 264 | return getNames(dut, "out") 265 | 266 | 267 | async def initDut(dut, inNames=None, outNames=None): 268 | """ 269 | Initializes a dut by adding a clock, setting initial valid and ready flags, 270 | and performing a reset. 271 | """ 272 | if (inNames is None): 273 | inNames = getInNames(dut) 274 | 275 | if (outNames is None): 276 | outNames = getOutNames(dut) 277 | 278 | ins, outs = getPorts(dut, inNames, outNames) 279 | 280 | # Create a 10us period clock on port clock 281 | clock = cocotb.clock.Clock(dut.clock, 10, units="us") 282 | cocotb.start_soon(clock.start()) # Start the clock 283 | 284 | for i in ins: 285 | i.setValid(0) 286 | 287 | for o in outs: 288 | o.setReady(1) 289 | 290 | # Reset 291 | dut.reset.value = 1 292 | await RisingEdge(dut.clock) 293 | dut.reset.value = 0 294 | await RisingEdge(dut.clock) 295 | return ins, outs 296 | 297 | 298 | class Stream: 299 | """ 300 | Class that encapsulates all the handshake ports for a stream 301 | """ 302 | 303 | def __init__(self, dataPort): 304 | self.dataPort = dataPort 305 | 306 | async def sendData(self, data): 307 | ds = cocotb.start_soon(self.dataPort.send((data, 0))) 308 | await ds 309 | 310 | def _buildSentinel(self, fields): 311 | if not isinstance(fields, list): 312 | return 0 313 | return tuple([self._buildSentinel(f) for f in fields]) 314 | 315 | async def sendEOS(self): 316 | data = cocotb.start_soon( 317 | self.dataPort.send((self._buildSentinel(self.dataPort.fields[0]), 1))) 318 | await data 319 | 320 | async def sendAndTerminate(self, data): 321 | for d in data: 322 | await self.sendData(d) 323 | 324 | await self.sendEOS() 325 | 326 | async def checkOutputs(self, results): 327 | resWithEOS = [(d, 0) for d in results] 328 | data = cocotb.start_soon(self.dataPort.checkOutputs(resWithEOS)) 329 | await data 330 | [(_, eos)] = await cocotb.start_soon(self.dataPort.collectNOutputs(1)) 331 | assert eos == 1 332 | -------------------------------------------------------------------------------- /lib/Dialect/Stream/StreamOps.cpp: -------------------------------------------------------------------------------- 1 | //===- StreamOps.cpp - Stream dialect ops -----------------------*- C++ -*-===// 2 | // 3 | // This file is licensed under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | #include "circt-stream/Dialect/Stream/StreamOps.h" 10 | 11 | #include 12 | 13 | #include "circt-stream/Dialect/Stream/StreamDialect.h" 14 | #include "circt-stream/Dialect/Stream/StreamTypes.h" 15 | #include "mlir/IR/Builders.h" 16 | #include "mlir/IR/OpImplementation.h" 17 | #include "mlir/Support/LogicalResult.h" 18 | #include "llvm/ADT/TypeSwitch.h" 19 | 20 | using namespace mlir; 21 | using namespace circt_stream; 22 | using namespace circt_stream::stream; 23 | 24 | #define GET_OP_CLASSES 25 | #include "circt-stream/Dialect/Stream/StreamOps.cpp.inc" 26 | 27 | static LogicalResult verifyRegionArgs(Operation *op, TypeRange expectedTypes, 28 | Region &r) { 29 | if (r.getNumArguments() != expectedTypes.size()) 30 | return op->emitError("expect region to have ") 31 | << expectedTypes.size() << " arguments."; 32 | 33 | unsigned i = 0; 34 | for (auto [expected, actual] : 35 | llvm::zip(expectedTypes, r.getArgumentTypes())) { 36 | if (expected != actual) 37 | return op->emitError("expect the block argument #") 38 | << i << " to have type " << expected << ", got " << actual 39 | << " instead."; 40 | i++; 41 | } 42 | 43 | return success(); 44 | } 45 | 46 | static LogicalResult verifyYieldOperands(Operation *op, TypeRange returnTypes, 47 | Region &r) { 48 | for (auto term : r.getOps()) { 49 | if (term.getNumOperands() != returnTypes.size()) 50 | return term.emitError("expect ") 51 | << returnTypes.size() << " operands, got " 52 | << term.getNumOperands(); 53 | unsigned i = 0; 54 | for (auto [expected, actual] : 55 | llvm::zip(returnTypes, term.getOperandTypes())) { 56 | if (expected != actual) 57 | return term.emitError("expect the operand #") 58 | << i << " to have type " << expected << ", got " << actual 59 | << " instead."; 60 | i++; 61 | } 62 | } 63 | return success(); 64 | } 65 | 66 | static Type getElementType(Type streamType) { 67 | assert(streamType.isa() && 68 | "can only extract element type of a StreamType"); 69 | return streamType.cast().getElementType(); 70 | } 71 | 72 | /// Verifies that a region has indeed the expected inputs and that all 73 | /// terminators return operands matching the provided return types. 74 | static LogicalResult verifyRegion(Operation *op, Region &r, 75 | TypeRange inputTypes, TypeRange returnTypes) { 76 | // Check arguments 77 | if (failed(verifyRegionArgs(op, inputTypes, r))) 78 | return failure(); 79 | 80 | // Check terminator 81 | if (failed(verifyYieldOperands(op, returnTypes, r))) 82 | return failure(); 83 | 84 | return success(); 85 | } 86 | 87 | /// Verifies that a region can handle the input streams and always returns 88 | /// elements with the type of the output stream. 89 | static LogicalResult verifyDefaultRegion(Operation *op, Region &r, 90 | TypeRange registerTypes = {}) { 91 | // TODO where to append them? 92 | SmallVector inputTypes = 93 | llvm::to_vector(llvm::map_range(op->getOperandTypes(), getElementType)); 94 | llvm::copy(registerTypes, std::back_inserter(inputTypes)); 95 | 96 | SmallVector returnTypes = 97 | llvm::to_vector(llvm::map_range(op->getResultTypes(), getElementType)); 98 | llvm::copy(registerTypes, std::back_inserter(returnTypes)); 99 | 100 | return verifyRegion(op, r, inputTypes, returnTypes); 101 | } 102 | 103 | template 104 | static LogicalResult getRegTypes(TOp op, SmallVectorImpl ®Types) { 105 | Optional regTypeAttr = op.getRegisters(); 106 | if (regTypeAttr.has_value()) { 107 | for (auto attr : *regTypeAttr) { 108 | auto res = llvm::TypeSwitch(attr) 109 | .Case([&](auto intAttr) { 110 | regTypes.push_back(intAttr.getType()); 111 | return success(); 112 | }) 113 | .Default([&](Attribute) { 114 | return op->emitError("unsupported register type"); 115 | }); 116 | if (failed(res)) 117 | return failure(); 118 | } 119 | } 120 | return success(); 121 | } 122 | 123 | LogicalResult MapOp::verifyRegions() { 124 | SmallVector regTypes; 125 | if (failed(getRegTypes(*this, regTypes))) 126 | return failure(); 127 | 128 | return verifyDefaultRegion(getOperation(), getRegion(), regTypes); 129 | } 130 | 131 | LogicalResult FilterOp::verifyRegions() { 132 | SmallVector inputTypes = llvm::to_vector( 133 | llvm::map_range((*this)->getOperandTypes(), getElementType)); 134 | 135 | SmallVector regTypes; 136 | if (failed(getRegTypes(*this, regTypes))) 137 | return failure(); 138 | llvm::copy(regTypes, std::back_inserter(inputTypes)); 139 | 140 | SmallVector resultTypes; 141 | resultTypes.push_back(IntegerType::get(this->getContext(), 1)); 142 | llvm::copy(regTypes, std::back_inserter(resultTypes)); 143 | 144 | return verifyRegion(getOperation(), getRegion(), inputTypes, resultTypes); 145 | } 146 | 147 | LogicalResult ReduceOp::verifyRegions() { 148 | SmallVector regTypes; 149 | if (failed(getRegTypes(*this, regTypes))) 150 | return failure(); 151 | 152 | Type inputType = getElementType(getInput().getType()); 153 | Type accType = getElementType(getResult().getType()); 154 | 155 | SmallVector inputTypes = {accType, inputType}; 156 | llvm::copy(regTypes, std::back_inserter(inputTypes)); 157 | 158 | SmallVector resultTypes = {accType}; 159 | llvm::copy(regTypes, std::back_inserter(resultTypes)); 160 | 161 | return verifyRegion(getOperation(), getRegion(), inputTypes, resultTypes); 162 | } 163 | 164 | ParseResult UnpackOp::parse(OpAsmParser &parser, OperationState &result) { 165 | OpAsmParser::UnresolvedOperand tuple; 166 | TupleType type; 167 | 168 | if (parser.parseOperand(tuple) || 169 | parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() || 170 | parser.parseType(type)) 171 | return failure(); 172 | 173 | if (parser.resolveOperand(tuple, type, result.operands)) 174 | return failure(); 175 | 176 | result.addTypes(type.getTypes()); 177 | 178 | return success(); 179 | } 180 | 181 | void UnpackOp::print(OpAsmPrinter &p) { 182 | p << " " << getInput(); 183 | p.printOptionalAttrDict((*this)->getAttrs()); 184 | p << " : " << getInput().getType(); 185 | } 186 | 187 | /// Replaces unnecessary `stream.unpack` when its operand is the result of a 188 | /// `stream.pack` operation. In the following snippet, all uses of `%a2` and 189 | /// `%b2` are replaced with `%a` and `%b` respectively. 190 | /// 191 | /// ``` 192 | /// %tuple = stream.pack %a, %b {attributes} : tuple 193 | /// %a2, %b2 = stream.unpack %tuple {attributes} : tuple 194 | /// // ... some ops using %a2, %b2 195 | /// ``` 196 | LogicalResult UnpackOp::canonicalize(UnpackOp op, PatternRewriter &rewriter) { 197 | PackOp tuple = dyn_cast_or_null(op.getInput().getDefiningOp()); 198 | if (!tuple) 199 | return failure(); 200 | 201 | rewriter.replaceOp(op, tuple.getInputs()); 202 | return success(); 203 | } 204 | 205 | ParseResult PackOp::parse(OpAsmParser &parser, OperationState &result) { 206 | SmallVector operands; 207 | llvm::SMLoc allOperandLoc = parser.getCurrentLocation(); 208 | TupleType type; 209 | 210 | if (parser.parseOperandList(operands) || 211 | parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() || 212 | parser.parseType(type)) 213 | return failure(); 214 | 215 | if (parser.resolveOperands(operands, type.getTypes(), allOperandLoc, 216 | result.operands)) 217 | return failure(); 218 | 219 | result.addTypes(type); 220 | 221 | return success(); 222 | } 223 | 224 | void PackOp::print(OpAsmPrinter &p) { 225 | p << " " << getInputs(); 226 | p.printOptionalAttrDict((*this)->getAttrs()); 227 | p << " : " << getResult().getType(); 228 | } 229 | 230 | /// Replaces an unnecessary `stream.pack` when it's operands are results of a 231 | /// `stream.unpack` op. In the following snippet, all uses of `%tuple2` are 232 | /// replaced with `%tuple`. 233 | /// 234 | /// ``` 235 | /// %a, %b = stream.unpack %tuple {attributes} : tuple 236 | /// %tuple2 = stream.pack %a, %b {attributes} : tuple 237 | /// // ... some ops using %tuple2 238 | /// ``` 239 | LogicalResult PackOp::canonicalize(PackOp op, PatternRewriter &rewriter) { 240 | if (op.getInputs().size() == 0) 241 | return failure(); 242 | 243 | Operation *singleDefiningOp = op.getInputs()[0].getDefiningOp(); 244 | 245 | if (!llvm::all_of(op.getInputs(), [singleDefiningOp](Value input) { 246 | return input.getDefiningOp() == singleDefiningOp; 247 | })) 248 | return failure(); 249 | 250 | UnpackOp unpackDefiningOp = dyn_cast_or_null(singleDefiningOp); 251 | 252 | if (!unpackDefiningOp) 253 | return failure(); 254 | 255 | rewriter.replaceOp(op, unpackDefiningOp.getInput()); 256 | return success(); 257 | } 258 | 259 | LogicalResult SplitOp::verifyRegions() { 260 | SmallVector regTypes; 261 | if (failed(getRegTypes(*this, regTypes))) 262 | return failure(); 263 | return verifyDefaultRegion(getOperation(), getRegion(), regTypes); 264 | } 265 | 266 | LogicalResult CombineOp::verifyRegions() { 267 | SmallVector regTypes; 268 | if (failed(getRegTypes(*this, regTypes))) 269 | return failure(); 270 | return verifyDefaultRegion(getOperation(), getRegion(), regTypes); 271 | } 272 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: 3 | ============================================================================== 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | 182 | APPENDIX: How to apply the Apache License to your work. 183 | 184 | To apply the Apache License to your work, attach the following 185 | boilerplate notice, with the fields enclosed by brackets "[]" 186 | replaced with your own identifying information. (Don't include 187 | the brackets!) The text should be enclosed in the appropriate 188 | comment syntax for the file format. We also recommend that a 189 | file or class name and description of purpose be included on the 190 | same "printed page" as the copyright notice for easier 191 | identification within third-party archives. 192 | 193 | Copyright [yyyy] [name of copyright owner] 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | 207 | 208 | ---- LLVM Exceptions to the Apache 2.0 License ---- 209 | 210 | As an exception, if, as a result of your compiling your source code, portions 211 | of this Software are embedded into an Object form of such source code, you 212 | may redistribute such embedded portions in such Object form without complying 213 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 214 | 215 | In addition, if you combine or link compiled forms of this Software with 216 | software that is licensed under the GPLv2 ("Combined Software") and if a 217 | court of competent jurisdiction determines that the patent provision (Section 218 | 3), the indemnity provision (Section 9) or other Section of the License 219 | conflicts with the conditions of the GPLv2, you may retroactively and 220 | prospectively choose to deem waived or otherwise exclude such Section(s) of 221 | the License, but only in their entirety and only with respect to the Combined 222 | Software. 223 | 224 | ============================================================================== 225 | Software from third parties included in the LLVM Project: 226 | ============================================================================== 227 | The LLVM Project contains third party software which is under different license 228 | terms. All such code will be identified clearly using at least one of two 229 | mechanisms: 230 | 1) It will be in a separate directory tree with its own `LICENSE.txt` or 231 | `LICENSE` file at the top containing the specific license and restrictions 232 | which apply to that software, or 233 | 2) It will contain specific license and restriction terms at the top of every 234 | file. 235 | 236 | ============================================================================== 237 | Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): 238 | ============================================================================== 239 | University of Illinois/NCSA 240 | Open Source License 241 | 242 | Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. 243 | All rights reserved. 244 | 245 | Developed by: 246 | 247 | LLVM Team 248 | 249 | University of Illinois at Urbana-Champaign 250 | 251 | http://llvm.org 252 | 253 | Permission is hereby granted, free of charge, to any person obtaining a copy of 254 | this software and associated documentation files (the "Software"), to deal with 255 | the Software without restriction, including without limitation the rights to 256 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 257 | of the Software, and to permit persons to whom the Software is furnished to do 258 | so, subject to the following conditions: 259 | 260 | * Redistributions of source code must retain the above copyright notice, 261 | this list of conditions and the following disclaimers. 262 | 263 | * Redistributions in binary form must reproduce the above copyright notice, 264 | this list of conditions and the following disclaimers in the 265 | documentation and/or other materials provided with the distribution. 266 | 267 | * Neither the names of the LLVM Team, University of Illinois at 268 | Urbana-Champaign, nor the names of its contributors may be used to 269 | endorse or promote products derived from this Software without specific 270 | prior written permission. 271 | 272 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 273 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 274 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 275 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 276 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 277 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 278 | SOFTWARE. 279 | --------------------------------------------------------------------------------