profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/seldridge/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
Schuyler Eldridge seldridge @SiFive New York, NY seldridge.dev Hardware compiler hacker. Sometimes RISC-V accelerator builder. PhD from @bu-icsg.

bu-icsg/dana 170

Dynamically Allocated Neural Network Accelerator for the RISC-V Rocket Microprocessor in Chisel

chipsalliance/treadle 93

Chisel/Firrtl execution engine

freechipsproject/www.chisel-lang.org 18

The home of the Chisel3 website

seldridge/chisel-paradigms 5

Chisel examples shown for different programming paradigms

bu-icsg/latex-base 3

Base environment for building LaTeX papers, presentations, and posters

seldridge/cocktails 3

Cocktail Menu

bu-icsg/rocket-chip 1

Rocket Chip Generator

seldridge/algorithms 1

Software engineering practice, algorithms, and data structures

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 struct FirMemory {            numReadWritePorts == rhs.numReadWritePorts &&            dataWidth == rhs.dataWidth && depth == rhs.depth &&            readLatency == rhs.readLatency && writeLatency == rhs.writeLatency &&-           readUnderWrite == rhs.readUnderWrite;+           readUnderWrite == rhs.readUnderWrite &&+           writeUnderWrite == rhs.writeUnderWrite;   } }; } // namespace  static std::string getFirMemoryName(const FirMemory &mem) {-  return llvm::formatv("FIRRTLMem_{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}",+  return llvm::formatv("FIRRTLMem_{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}",

The naming will now also include an optional {9} position in the name that includes a string representation of how the clocks are attached to write ports. If the first and third write ports are the same clock, but the second is different, you'll get _aba. If they're all the same, you'll get aaa.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 struct FirMemory {   size_t readLatency;   size_t writeLatency;   size_t readUnderWrite;+  size_t writeUnderWrite;+  llvm::EquivalenceClasses<unsigned> writeSets; };++class SymbolDigger {

Removed due to the rework.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 struct FirMemory {   size_t readLatency;   size_t writeLatency;   size_t readUnderWrite;+  size_t writeUnderWrite;+  llvm::EquivalenceClasses<unsigned> writeSets; };++class SymbolDigger {+  DenseMap<Attribute, SmallVector<Operation *, 1>> internal;++public:+  SymbolDigger(ModuleOp module) {+    module.walk([&](Operation *op) {

This SymbolDigger is now gone. Clock information is found during LowerToHW when the firrtl.mem is being examined. There is then no need for any symbol chasing.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 void HWMemSimImplPass::runOnOperation() {     if (genOp.descriptor() == "FIRRTL_Memory") {       auto mem = analyzeMemOp(oldModule); +      // If this memory has defined write-after-write behavior, we need to+      // determine which sets of write ports are driven by the same clock.+      if (mem.writeUnderWrite == 1) {++        auto symbol = SymbolRefAttr::get(oldModule.getNameAttr());+        auto instances = symbolDigger.get(symbol);+        bool differentClocks = false;+        auto buildWriteSets = [&](Operation *op) {+          llvm::DenseMap<Value, unsigned> clockToLeader;+          llvm::EquivalenceClasses<unsigned> writeSets;+          for (size_t i = mem.numReadPorts * 3 + 0, e = op->getNumOperands();+               i < e; i += 5) {

All this is now gone. 😄 This now uses actual write port indices in LowerToHW.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 void HWMemSimImplPass::runOnOperation() {     if (genOp.descriptor() == "FIRRTL_Memory") {       auto mem = analyzeMemOp(oldModule); +      // If this memory has defined write-after-write behavior, we need to+      // determine which sets of write ports are driven by the same clock.+      if (mem.writeUnderWrite == 1) {++        auto symbol = SymbolRefAttr::get(oldModule.getNameAttr());+        auto instances = symbolDigger.get(symbol);+        bool differentClocks = false;+        auto buildWriteSets = [&](Operation *op) {+          llvm::DenseMap<Value, unsigned> clockToLeader;

This was moved into LowerToHW and now uses a SmallDenseMap.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 void HWMemSimImplPass::runOnOperation() {     if (genOp.descriptor() == "FIRRTL_Memory") {       auto mem = analyzeMemOp(oldModule); +      // If this memory has defined write-after-write behavior, we need to+      // determine which sets of write ports are driven by the same clock.+      if (mem.writeUnderWrite == 1) {++        auto symbol = SymbolRefAttr::get(oldModule.getNameAttr());+        auto instances = symbolDigger.get(symbol);+        bool differentClocks = false;+        auto buildWriteSets = [&](Operation *op) {

This was removed in the rebase.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 void HWMemSimImplPass::runOnOperation() {     if (genOp.descriptor() == "FIRRTL_Memory") {       auto mem = analyzeMemOp(oldModule); +      // If this memory has defined write-after-write behavior, we need to+      // determine which sets of write ports are driven by the same clock.+      if (mem.writeUnderWrite == 1) {

Removed entirely due to a rework of this PR.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 struct FirMemory {   size_t readLatency;   size_t writeLatency;   size_t readUnderWrite;+  size_t writeUnderWrite;+  llvm::EquivalenceClasses<unsigned> writeSets; };++class SymbolDigger {+  DenseMap<Attribute, SmallVector<Operation *, 1>> internal;++public:+  SymbolDigger(ModuleOp module) {+    module.walk([&](Operation *op) {+      TypeSwitch<Operation *, void>(op)+          .Case<hw::InstanceOp>([&](hw::InstanceOp op) {+            internal[op.moduleNameAttr()].push_back(op);

This is now gone after the reworking this to add clock information during LowerToHW.

seldridge

comment created time in 2 days

PullRequestReviewEvent

push eventllvm/circt

Schuyler Eldridge

commit sha 15bbcf727f1f7029122d3e0a9dd291ed934443f7

fixup! [FIRRTL][HW] Use WUW lowering for FIRRTL mems

view details

push time in 2 days

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 void HWMemSimImplPass::runOnOperation() {     if (genOp.descriptor() == "FIRRTL_Memory") {       auto mem = analyzeMemOp(oldModule); +      // If this memory has defined write-after-write behavior, we need to+      // determine which sets of write ports are driven by the same clock.+      if (mem.writeUnderWrite == 1) {++        auto symbol = SymbolRefAttr::get(oldModule.getNameAttr());+        auto instances = symbolDigger.get(symbol);+        bool differentClocks = false;+        auto buildWriteSets = [&](Operation *op) {+          llvm::DenseMap<Value, unsigned> clockToLeader;+          llvm::EquivalenceClasses<unsigned> writeSets;+          for (size_t i = mem.numReadPorts * 3 + 0, e = op->getNumOperands();+               i < e; i += 5) {+            Value clock = op->getOperand(i);+            auto result = clockToLeader.insert({clock, i});+            // Clock not known+            if (result.second)+              writeSets.insert(i);+            // Clock already known+            else+              writeSets.unionSets(result.first->second, i);+          }+          return writeSets;+        };++        // Loop over each instance of the memory.  Save the write port mapping

Updated to do it this way in the rebase. Much cleaner. Great suggestion.

seldridge

comment created time in 2 days

PullRequestReviewEvent

push eventllvm/circt

John Demme

commit sha 67fd0f8e6d34d90ab27f96da37f193d8eb644e6a

[MSFT] Python bindings for ops and changes to make them work - Adds python bindings for MSFTModuleOp and InstanceOp. - Re-use as much as possible from `circt.hw._hw_ops_ext`. Required changes to ODS definitions. - Found and fixed some bugs.

view details

Morten Borup Petersen

commit sha eff81822c1caf480b98c9383b42e38c406695f9c

[Handshake] Fix lowering of std.CallOp to handshake.InstanceOp (#1538) The PR adds a wrapRewriter which wraps the partial lowering functions already in place (such as replaceCallOps, addForkOps...). This rewriter composes a bit of supporting code (lowerToHandshake, PartialLowerFuncOp) that creates an instance of a ConversionTarget as well as an application of applyPartialConversion. The end result is that any rewriting and their side effects (most importantly modifications to referencing SSA values) made within each of the partial lowering functions are fully "comitted" after a partial lowering function returns. While being a bit hacky, I suspect that this structure might be generally useful for conversion passes which is heavily dependent on some sequence of transformations being applied in a specific order, where the author does not want to write distinct transformation passes for each of these steps.

view details

Chris Lattner

commit sha 97f3149214cafeea79e505879d9caeae40f723c0

[HW] Split custom attributes out to their own .td file. NFC.

view details

Chris Lattner

commit sha 471e60ceafc8dc43fa7e582ca35d5830ed6a0aa9

[HW] Add support for parameter decls to the hw.module* operations. This adds IR support to parse and print them, but there is no export verilog support yet.

view details

Chris Lattner

commit sha 8b866609f71fc21a4bd7e9bda65a2497108b5887

[tests] remove the top level MLIR module from this test, NFC. It is just indenting things for no reason.

view details

Chris Lattner

commit sha 3cbdf1f1391bab2dbaad5ccd4d69c3e6f5e02ecc

[HW] Split inline lambdas out to named lambdas, NFC. This makes the code easier to read, and will hopefully fix a clang-format grumpiness on a builder.

view details

Chris Lattner

commit sha bb9781ce49980d80acf093de5986722e439c9920

[ExportVerilog] Remove #ifdef'd out code I inadvertantly committed, NFC.

view details

Schuyler Eldridge

commit sha 740f2f7aad1e0929a9a65bd1a7bfcb0da82b5508

[FIRRTL] Add write-under-write enum attr Add a new enumerated attribute that defines the write-under-write behavior of FIRRTL memories. This is not defined in the FIRRTL specification, but is necessary to accurately differentiate how FIRRTL memories should behave. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>

view details

Schuyler Eldridge

commit sha 5fc5585aaa32c24add98ce594399bd8f60945680

[FIRRTL][HW] Use WUW lowering for FIRRTL mems Change the way that FIRRTL memories are lowered to HW memories to propagate the write-under-write behavior. Modify HWMemSimImpl (which generate a simulation model of a memory) to respect a new write-under-write attribute. This new write-under-write attribute can either be "undefined" indicating that the resulting simulation memory should put each write port in an independent always block, regardless of whether or not they are on the same clock. If the write-under-write behavior is set to "port order" then write ports that are trivially driven by the same clock are put in the same always block and ordered sequentially by port order. I.e., later ports will override earlier ports if they both write to the same address. This behavior is added because the Scala FIRRTL Compiler (SFC) will lower write ports in this way and a deviation from it is a formal equivalence mismatch. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>

view details

Schuyler Eldridge

commit sha a0b1b323b7881d7c1ab759369e01d9c6746b99fa

[HW] Add test of write-under-write sim mems, NFC Add tests of "port order" write-under-write behavior for simulation memory generation. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>

view details

push time in 2 days

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 void HWMemSimImplPass::runOnOperation() {     if (genOp.descriptor() == "FIRRTL_Memory") {       auto mem = analyzeMemOp(oldModule); +      // If this memory has defined write-after-write behavior, we need to+      // determine which sets of write ports are driven by the same clock.+      if (mem.writeUnderWrite == 1) {++        auto symbol = SymbolRefAttr::get(oldModule.getNameAttr());+        auto instances = symbolDigger.get(symbol);+        bool differentClocks = false;+        auto buildWriteSets = [&](Operation *op) {+          llvm::DenseMap<Value, unsigned> clockToLeader;+          llvm::EquivalenceClasses<unsigned> writeSets;+          for (size_t i = mem.numReadPorts * 3 + 0, e = op->getNumOperands();+               i < e; i += 5) {+            Value clock = op->getOperand(i);+            auto result = clockToLeader.insert({clock, i});+            // Clock not known+            if (result.second)+              writeSets.insert(i);+            // Clock already known+            else+              writeSets.unionSets(result.first->second, i);+          }+          return writeSets;+        };++        // Loop over each instance of the memory.  Save the write port mapping

That makes sense. This would also would, I think, work around the problem of memories getting incorrectly deduplicated because the analysis would happen during schema generation.

Any idea on how to encode the disjoint set clock information into an attribute? An arrayattr of clock ids?

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 void HWMemSimImplPass::runOnOperation() {     if (genOp.descriptor() == "FIRRTL_Memory") {       auto mem = analyzeMemOp(oldModule); +      // If this memory has defined write-after-write behavior, we need to+      // determine which sets of write ports are driven by the same clock.+      if (mem.writeUnderWrite == 1) {++        auto symbol = SymbolRefAttr::get(oldModule.getNameAttr());+        auto instances = symbolDigger.get(symbol);+        bool differentClocks = false;+        auto buildWriteSets = [&](Operation *op) {+          llvm::DenseMap<Value, unsigned> clockToLeader;+          llvm::EquivalenceClasses<unsigned> writeSets;+          for (size_t i = mem.numReadPorts * 3 + 0, e = op->getNumOperands();+               i < e; i += 5) {

There should be a better way to handle this. This is iterating over the clock operands in the generator op adhering to the FIRRTL_Memory schema. The schema is parametric, but organized as flat operands of n read ports followed by m write ports followed by p readwrite ports. The iteration over this is cleaner earlier in this file because all the ports are walked in order where they're skipped here.

I'll see how to fix this. At minimum, just changing the loop iterator to go from 0 to numwriteports - 1 would definitely clear up what's going on here.

seldridge

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

 struct FirMemory {            numReadWritePorts == rhs.numReadWritePorts &&            dataWidth == rhs.dataWidth && depth == rhs.depth &&            readLatency == rhs.readLatency && writeLatency == rhs.writeLatency &&-           readUnderWrite == rhs.readUnderWrite;+           readUnderWrite == rhs.readUnderWrite &&+           writeUnderWrite == rhs.writeUnderWrite;   } }; } // namespace  static std::string getFirMemoryName(const FirMemory &mem) {-  return llvm::formatv("FIRRTLMem_{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}",+  return llvm::formatv("FIRRTLMem_{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}",

No, we're totally different. SFC generates logic for memories which were not blackboxed as inline logic in the module. There should be no issue in picking a convention for what we want to call the memory.

seldridge

comment created time in 2 days

PullRequestReviewEvent

issue commentchipsalliance/chisel3

Fail to use `addSource` for objects having the same value

The issue is because if those values are the same, Inner1 and Inner2 will deduplicate to the same module. You can work around this by disabling deduplication for those modules. Try changing the addSource methods to:

addSource(value, "inner1", disableDedup=true)
/* ... */
addSource(value, "inner2", disableDedup=true)

Scastie showing this working: https://scastie.scala-lang.org/0egkINozRsyXKvsXfxDvqg

The fundamental problem is that the wiring transform needs to have a unique source driver for each sink. If it doesn't have this, then it gets confused and errors out (with that message you see). The bore API is a bit safer than addSource/addSink and adds those deduplication flags to work around this exact issue.

retrhelo

comment created time in 2 days

issue commentllvm/circt

[FIRRTL] SFC Memory Lowering: Respect Write Collision Behavior

With #1805, the above will be lowered to:

  always @(posedge ro_clock_0) begin
    _T <= ro_en_0;
    _T_0 <= ro_addr_0;
  end // always @(posedge)
  always @(posedge wo_clock_0) begin
    if (wo_en_0 & wo_mask_0)
      Memory[wo_addr_0] <= wo_data_0;
    if (wo_en_1 & wo_mask_1)
      Memory[wo_addr_1] <= wo_data_1;
  end // always @(posedge)

If instead the second memory port was driven by a clock2, then you get split always blocks:

  always @(posedge wo_clock_0) begin
    if (wo_en_0 & wo_mask_0)
      Memory[wo_addr_0] <= wo_data_0;
  end // always @(posedge)
  always @(posedge wo_clock_1) begin
    if (wo_en_1 & wo_mask_1)
      Memory[wo_addr_1] <= wo_data_1;
  end // always @(posedge)
seldridge

comment created time in 3 days

PR opened llvm/circt

[FIRRTL][HW] Add Support for "Port Order" Write-Under-Write to HWMemSimImpl, Use for FIRRTL Lowering

Fixes #1757.

This PR changes the lowering for FIRRTL memories. Previously, each write port on the same clock would be emitted into a separate always block. This resulted in Verilog that behaviorally described "undefined" write-under-write behavior. I.e., it described a memory where any write port could "win" in a write collision (where two ports write to the same address) and would likely have written an x to the memory in simulation.

FIRRTL memories have "port order" write-under-write behavior for ports on the same clock. This means that if multiple ports on the same clock write to the same memory address, the highest numbered port will win. In effect, this is explicitly encoding an ordering which a synthesis tool must respect. This was then producing formal equivalence mismatches that @drom was seeing (and I reported in #1757).

The FIRRTL specification is entirely silent about this behavior, so we're relying on what the Scala FIRRTL Compiler empirically does here.

This PR adds some infrastructure for describing the write-under-write behavior. There's a new FIRRTL enum attribute (which can be either "undefined" or "port order"). This is then plumbed through and used to set a "write-under-write" attribute in the FIRRTLMemory schema. The HWMemSimImpl pass is then updated to lower "undefined" the same as it does now and to lower "port order" correctly.

To determine the grouping of ports, this uses a trivial check of looking to see what value drives a memory write port clock. All instances of the memory must have the same write port clock driving. Each set of write ports is then emitted into a separate always block where writes are ordered based on port order.

+234 -42

0 comment

6 changed files

pr created time in 3 days

create barnchllvm/circt

branch : dev/seldridge/issue-1757

created branch time in 3 days