You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by GitBox <gi...@apache.org> on 2021/06/18 20:58:00 UTC
[GitHub] [tvm-vta] adavare opened a new pull request #30: Chisel Pipelined GEMM
adavare opened a new pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30
A CHISEL pipelined GEMM module along with associated unit tests are being added with this pull request.
Significant cycle time improvements are seen for conv2D workloads. In particular, when evaluated with "tvm/vta/tutorials/frontend/deploy_classification.py" with the tsim target using all 6 supported ResNets, overall speedups range from 3.10x to 3.69x as shown in the table below. Top 5 labels all match for the default "cat.png" image in all cases.
| Net | Baseline, cycles | With Pipelined GEMM, cycles | Speedup |
| :------------: | --------------------: | -----------------: | -------: |
| resnet18_v1 | 27,679,515 | 7,729,601 | 3.58 |
| resnet34_v1 | 57,471,735 | 15,736,441 | 3.65 |
| resnet18_v2 | 27,558,980 | 7,608,808 | 3.62 |
| resnet34_v2 | 57,270,321 | 15,534,528 | 3.69 |
| resnet50_v2 | 68,187,407 | 22,012,906 | 3.10 |
| resnet101_v2 | 129,263,035 | 39,182,158 | 3.30 |
Code contributions to this PR were made by the following individuals (in alphabetical order): @suvadeep89, @stevenmburns, @pasqoc, @adavare, @sjain12intel, @aasorokiin, and @zhenkuny.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] vegaluisjose commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
vegaluisjose commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r654967399
##########
File path: hardware/chisel/src/test/scala/unittest/TensorGemmJsonTest.scala
##########
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package unittest
+
+import chisel3._
+import chisel3.util._
+import chisel3.iotesters.PeekPokeTester
+import unittest.util._
+import vta.core._
+import vta.util.config._
+
+import scala.io._
+import scala.language.postfixOps
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
+
+class TensorGemmJsonTester(c: TensorGemmPipelinedSplit, fn : String = "/x.json")
+ extends PeekPokeTester(c) {
+
+ val bufferedSource = Source.fromURL(getClass.getResource(fn))
+ val mapper = new ObjectMapper() with ScalaObjectMapper
+ mapper.registerModule(DefaultScalaModule)
+ val archState = mapper.readValue[Map[String, Object]](bufferedSource.reader())
+ bufferedSource.close
+
+ val inst = archState("inst").asInstanceOf[Map[String,String]]
+
+ def build_scratchpad(tag: String) : Array[Array[BigInt]] = {
+ val arr = archState(tag).asInstanceOf[Seq[Map[String,Object]]]
+ (
+ for {(m,i) <- arr zipWithIndex} yield {
+ val idx = BigInt(m("idx").asInstanceOf[String], 16)
+ assert(BigInt(i) == idx)
+ val vec = m("vec").asInstanceOf[Seq[String]]
+ (
+ for {v <- vec} yield {
+ BigInt(v, 16)
+ }
+ ).toArray
+ }
+ ).toArray
+ }
+
+ val inp_scratchpad = build_scratchpad("inp")
+ val wgt_scratchpad = build_scratchpad("wgt")
+ val uop_scratchpad = build_scratchpad("uop")
+ val acc_scratchpad = build_scratchpad("acc_i")
+ val acc_o_scratchpad = build_scratchpad("acc_o")
+
+ poke(c.io.start, 0)
+
+ val dec_reset = BigInt(inst("reset"), 16)
+ val uop_begin = BigInt(inst("uop_begin"), 16)
+ val uop_end = BigInt(inst("uop_end"), 16)
+ assert(uop_begin < uop_end)
+ val lp_0 = BigInt(inst("lp_0"), 16)
+ val lp_1 = BigInt(inst("lp_1"), 16)
+ val acc_0 = BigInt(inst("acc_0"), 16)
+ val inp_0 = BigInt(inst("inp_0"), 16)
+ val wgt_0 = BigInt(inst("wgt_0"), 16)
+ val acc_1 = BigInt(inst("acc_1"), 16)
+ val inp_1 = BigInt(inst("inp_1"), 16)
+ val wgt_1 = BigInt(inst("wgt_1"), 16)
+
+ poke(c.io.dec.reset, dec_reset)
+
+ poke(c.io.dec.uop_begin, uop_begin)
+ poke(c.io.dec.uop_end, uop_end)
+ poke(c.io.dec.lp_0, lp_0)
+ poke(c.io.dec.lp_1, lp_1)
+ poke(c.io.dec.acc_0, acc_0)
+ poke(c.io.dec.acc_1, acc_1)
+ poke(c.io.dec.inp_0, inp_0)
+ poke(c.io.dec.inp_1, inp_1)
+ poke(c.io.dec.wgt_0, wgt_0)
+ poke(c.io.dec.wgt_1, wgt_1)
+ // Don't need empty_0,{push,pop}_{next,prev},op
+
+ class TensorMasterMock(tm: TensorMaster, scratchpad : Array[Array[BigInt]]) {
+ poke(tm.rd(0).data.valid, 0)
+ var valid = peek(tm.rd(0).idx.valid)
+ var idx : Int = 0
+ def logical_step() {
+ if (valid == 1) {
+ poke(tm.rd(0).data.valid, 1)
+ val cols = tm.rd(0).data.bits(0).size
+ for {i <- 0 until tm.rd(0).data.bits.size
+ j <- 0 until cols
+ } {
+ poke(tm.rd(0).data.bits(i)(j), scratchpad(idx)(i*cols + j))
+ }
+ } else {
+ poke(tm.rd(0).data.valid, 0)
+ }
+ valid = peek(tm.rd(0).idx.valid)
+ idx = peek(tm.rd(0).idx.bits).toInt
+ }
+ }
+
+ class TensorMasterMockWr(tm: TensorMaster, scratchpad : Array[Array[BigInt]]) {
+ def logical_step() {
+ if (peek(tm.wr(0).valid) == 1) {
+ val idx = peek(tm.wr(0).bits.idx).toInt
+ val cols = tm.wr(0).bits.data(0).size
+ for {
+ i <- 0 until tm.wr(0).bits.data.size
+ j <- 0 until cols
+ } {
+ scratchpad(idx)(i*cols + j) = peek(tm.wr(0).bits.data(i)(j))
+ }
+ }
+ }
+ }
+
+ class UopMasterMock(um: UopMaster, scratchpad: Array[Array[BigInt]]) {
+ poke(um.data.valid, 0)
+ var valid = peek(um.idx.valid)
+ var idx : Int = 0
+ def logical_step() {
+ if (valid == 1) {
+ poke(um.data.valid, 1)
+ poke(um.data.bits.u0, scratchpad(idx)(0))
+ poke(um.data.bits.u1, scratchpad(idx)(1))
+ poke(um.data.bits.u2, scratchpad(idx)(2))
+ } else {
+ poke(um.data.valid, 0)
+ }
+ valid = peek(um.idx.valid)
+ idx = peek(um.idx.bits).toInt
+ }
+ }
+
+ class Mocks {
+ val uop_mock = new UopMasterMock(c.io.uop, uop_scratchpad)
+ val inp_mock = new TensorMasterMock(c.io.inp, inp_scratchpad)
+ val wgt_mock = new TensorMasterMock(c.io.wgt, wgt_scratchpad)
+ val acc_mock = new TensorMasterMock(c.io.acc, acc_scratchpad)
+ val acc_mock_wr = new TensorMasterMockWr(c.io.acc, acc_scratchpad)
+
+ val uop_indices = new scala.collection.mutable.Queue[BigInt]
+ val acc_indices = new scala.collection.mutable.Queue[BigInt]
+ val inp_indices = new scala.collection.mutable.Queue[BigInt]
+ val wgt_indices = new scala.collection.mutable.Queue[BigInt]
+ val accout_indices = new scala.collection.mutable.Queue[BigInt]
+ val out_indices = new scala.collection.mutable.Queue[BigInt]
+
+ def logical_step() {
+ step(1)
+ uop_mock.logical_step()
+ inp_mock.logical_step()
+ wgt_mock.logical_step()
+ acc_mock.logical_step()
+ acc_mock_wr.logical_step()
+
+ if (peek(c.io.uop.idx.valid) == 1) {
+ expect(c.io.uop.idx.bits, uop_indices.dequeue())
+ }
+ if (peek(c.io.acc.rd(0).idx.valid) == 1) {
+ expect(c.io.acc.rd(0).idx.bits, acc_indices.dequeue())
+ }
+ if (peek(c.io.inp.rd(0).idx.valid) == 1) {
+ expect(c.io.inp.rd(0).idx.bits, inp_indices.dequeue())
+ }
+ if (peek(c.io.wgt.rd(0).idx.valid) == 1) {
+ expect(c.io.wgt.rd(0).idx.bits, wgt_indices.dequeue())
+ }
+ if (peek(c.io.acc.wr(0).valid) == 1) {
+ expect(c.io.acc.wr(0).bits.idx, accout_indices.dequeue())
+ }
+ if (peek(c.io.out.wr(0).valid) == 1) {
+ expect(c.io.out.wr(0).bits.idx, out_indices.dequeue())
+ }
+ }
+
+ def test_if_done() {
+ println(s"uop_indices should be empty ${uop_indices.size}")
+ println(s"acc_indices should be empty ${acc_indices.size}")
+ println(s"inp_indices should be empty ${inp_indices.size}")
+ println(s"wgt_indices should be empty ${wgt_indices.size}")
+ println(s"accout_indices should be empty ${accout_indices.size}")
+ println(s"out_indices should be empty ${out_indices.size}")
+ }
+
+ def check() = {
+ val result = for {
+ ((x,y),idx) <- (acc_scratchpad, acc_o_scratchpad).zipped.toList.zipWithIndex
+ } yield {
+ (for {((xx,yy),jdx) <- (x,y).zipped.toList.zipWithIndex} yield {
+ if (xx != yy) {
+ println(s"Value mismatch at $idx $jdx: $xx (actual) != $yy (expected)")
+ }
+ xx == yy
+ }).reduce((x,y) => x&&y)
+ }
+ val result2 = result.reduce((x,y) => x&&y)
+ result2
+ }
+ }
+
+ val mocks = new Mocks
+
+ for {
+ cnt_o <- BigInt(0) until lp_0
+ cnt_i <- BigInt(0) until lp_1
+ uop_idx <- uop_begin until uop_end
+ } {
+ val u0 = uop_scratchpad(uop_idx.toInt)(0)
+ val u1 = uop_scratchpad(uop_idx.toInt)(1)
+ val u2 = uop_scratchpad(uop_idx.toInt)(2)
+
+ mocks.uop_indices.enqueue(uop_idx)
+ mocks.acc_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+ mocks.inp_indices.enqueue(u1 + inp_0*cnt_o + inp_1*cnt_i)
+ mocks.wgt_indices.enqueue(u2 + wgt_0*cnt_o + wgt_1*cnt_i)
+ mocks.accout_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+
+ if (dec_reset == 0) {
+ mocks.out_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+ }
+ }
+
+ poke(c.io.start, 0)
+ mocks.logical_step()
+ expect(c.io.state, c.sIdle)
+ poke(c.io.start, 1)
+
+ val total_steps = (uop_end-uop_begin)*lp_0*lp_1
+
+ val max_count = 100 + 4*total_steps
+ var count = 0
+ while (peek(c.io.done) == 0 && count < max_count) {
+ if (count % 100 == 0) {
+ println(s"logical_step $count")
+ }
+ mocks.logical_step()
+ if (count == 0) {
+ poke(c.io.start, 0)
+ }
+ count += 1
+ }
+
+ assert(peek(c.io.done) == 1, s"Signal done never high even after $count steps.")
+ println(s"Signal done high after $count steps.")
+
+ mocks.logical_step()
+ expect(c.io.done, 0)
+
+ val cc = mocks.check()
+ println(s"Checking acc with acc_o ${cc}")
+ assert(cc)
+
+ println(s"Total active steps: ${total_steps}")
+ mocks.test_if_done()
+}
+
+class TensorGemmJsonTestx extends GenericTest("TensorGemmJson", (p:Parameters) => new TensorGemmPipelinedSplit()(p),
+ (c:TensorGemmPipelinedSplit) => new TensorGemmJsonTester(c, "/x.json"))
+
+class TensorGemmJsonTesty extends GenericTest("TensorGemmJson", (p:Parameters) => new TensorGemmPipelinedSplit()(p),
+ (c:TensorGemmPipelinedSplit) => new TensorGemmJsonTester(c, "/y.json"))
Review comment:
Hey @adavare , nice PR.
can we change `x.json` and `y.json` to have more meaningful names? It looks like these are test-data...is there some information that `x` tests versus `y` that we could use for naming?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] liangfu commented on pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
liangfu commented on pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#issuecomment-865777956
I'm also curious about logic resource consumption for the simple GEMM and pipelined GEMM. Do you have numbers on that ?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] adavare commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
adavare commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r655546750
##########
File path: hardware/chisel/src/test/scala/unittest/TensorGemmJsonTest.scala
##########
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package unittest
+
+import chisel3._
+import chisel3.util._
+import chisel3.iotesters.PeekPokeTester
+import unittest.util._
+import vta.core._
+import vta.util.config._
+
+import scala.io._
+import scala.language.postfixOps
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
+
+class TensorGemmJsonTester(c: TensorGemmPipelinedSplit, fn : String = "/x.json")
+ extends PeekPokeTester(c) {
+
+ val bufferedSource = Source.fromURL(getClass.getResource(fn))
+ val mapper = new ObjectMapper() with ScalaObjectMapper
+ mapper.registerModule(DefaultScalaModule)
+ val archState = mapper.readValue[Map[String, Object]](bufferedSource.reader())
+ bufferedSource.close
+
+ val inst = archState("inst").asInstanceOf[Map[String,String]]
+
+ def build_scratchpad(tag: String) : Array[Array[BigInt]] = {
+ val arr = archState(tag).asInstanceOf[Seq[Map[String,Object]]]
+ (
+ for {(m,i) <- arr zipWithIndex} yield {
+ val idx = BigInt(m("idx").asInstanceOf[String], 16)
+ assert(BigInt(i) == idx)
+ val vec = m("vec").asInstanceOf[Seq[String]]
+ (
+ for {v <- vec} yield {
+ BigInt(v, 16)
+ }
+ ).toArray
+ }
+ ).toArray
+ }
+
+ val inp_scratchpad = build_scratchpad("inp")
+ val wgt_scratchpad = build_scratchpad("wgt")
+ val uop_scratchpad = build_scratchpad("uop")
+ val acc_scratchpad = build_scratchpad("acc_i")
+ val acc_o_scratchpad = build_scratchpad("acc_o")
+
+ poke(c.io.start, 0)
+
+ val dec_reset = BigInt(inst("reset"), 16)
+ val uop_begin = BigInt(inst("uop_begin"), 16)
+ val uop_end = BigInt(inst("uop_end"), 16)
+ assert(uop_begin < uop_end)
+ val lp_0 = BigInt(inst("lp_0"), 16)
+ val lp_1 = BigInt(inst("lp_1"), 16)
+ val acc_0 = BigInt(inst("acc_0"), 16)
+ val inp_0 = BigInt(inst("inp_0"), 16)
+ val wgt_0 = BigInt(inst("wgt_0"), 16)
+ val acc_1 = BigInt(inst("acc_1"), 16)
+ val inp_1 = BigInt(inst("inp_1"), 16)
+ val wgt_1 = BigInt(inst("wgt_1"), 16)
+
+ poke(c.io.dec.reset, dec_reset)
+
+ poke(c.io.dec.uop_begin, uop_begin)
+ poke(c.io.dec.uop_end, uop_end)
+ poke(c.io.dec.lp_0, lp_0)
+ poke(c.io.dec.lp_1, lp_1)
+ poke(c.io.dec.acc_0, acc_0)
+ poke(c.io.dec.acc_1, acc_1)
+ poke(c.io.dec.inp_0, inp_0)
+ poke(c.io.dec.inp_1, inp_1)
+ poke(c.io.dec.wgt_0, wgt_0)
+ poke(c.io.dec.wgt_1, wgt_1)
+ // Don't need empty_0,{push,pop}_{next,prev},op
+
+ class TensorMasterMock(tm: TensorMaster, scratchpad : Array[Array[BigInt]]) {
+ poke(tm.rd(0).data.valid, 0)
+ var valid = peek(tm.rd(0).idx.valid)
+ var idx : Int = 0
+ def logical_step() {
+ if (valid == 1) {
+ poke(tm.rd(0).data.valid, 1)
+ val cols = tm.rd(0).data.bits(0).size
+ for {i <- 0 until tm.rd(0).data.bits.size
+ j <- 0 until cols
+ } {
+ poke(tm.rd(0).data.bits(i)(j), scratchpad(idx)(i*cols + j))
+ }
+ } else {
+ poke(tm.rd(0).data.valid, 0)
+ }
+ valid = peek(tm.rd(0).idx.valid)
+ idx = peek(tm.rd(0).idx.bits).toInt
+ }
+ }
+
+ class TensorMasterMockWr(tm: TensorMaster, scratchpad : Array[Array[BigInt]]) {
+ def logical_step() {
+ if (peek(tm.wr(0).valid) == 1) {
+ val idx = peek(tm.wr(0).bits.idx).toInt
+ val cols = tm.wr(0).bits.data(0).size
+ for {
+ i <- 0 until tm.wr(0).bits.data.size
+ j <- 0 until cols
+ } {
+ scratchpad(idx)(i*cols + j) = peek(tm.wr(0).bits.data(i)(j))
+ }
+ }
+ }
+ }
+
+ class UopMasterMock(um: UopMaster, scratchpad: Array[Array[BigInt]]) {
+ poke(um.data.valid, 0)
+ var valid = peek(um.idx.valid)
+ var idx : Int = 0
+ def logical_step() {
+ if (valid == 1) {
+ poke(um.data.valid, 1)
+ poke(um.data.bits.u0, scratchpad(idx)(0))
+ poke(um.data.bits.u1, scratchpad(idx)(1))
+ poke(um.data.bits.u2, scratchpad(idx)(2))
+ } else {
+ poke(um.data.valid, 0)
+ }
+ valid = peek(um.idx.valid)
+ idx = peek(um.idx.bits).toInt
+ }
+ }
+
+ class Mocks {
+ val uop_mock = new UopMasterMock(c.io.uop, uop_scratchpad)
+ val inp_mock = new TensorMasterMock(c.io.inp, inp_scratchpad)
+ val wgt_mock = new TensorMasterMock(c.io.wgt, wgt_scratchpad)
+ val acc_mock = new TensorMasterMock(c.io.acc, acc_scratchpad)
+ val acc_mock_wr = new TensorMasterMockWr(c.io.acc, acc_scratchpad)
+
+ val uop_indices = new scala.collection.mutable.Queue[BigInt]
+ val acc_indices = new scala.collection.mutable.Queue[BigInt]
+ val inp_indices = new scala.collection.mutable.Queue[BigInt]
+ val wgt_indices = new scala.collection.mutable.Queue[BigInt]
+ val accout_indices = new scala.collection.mutable.Queue[BigInt]
+ val out_indices = new scala.collection.mutable.Queue[BigInt]
+
+ def logical_step() {
+ step(1)
+ uop_mock.logical_step()
+ inp_mock.logical_step()
+ wgt_mock.logical_step()
+ acc_mock.logical_step()
+ acc_mock_wr.logical_step()
+
+ if (peek(c.io.uop.idx.valid) == 1) {
+ expect(c.io.uop.idx.bits, uop_indices.dequeue())
+ }
+ if (peek(c.io.acc.rd(0).idx.valid) == 1) {
+ expect(c.io.acc.rd(0).idx.bits, acc_indices.dequeue())
+ }
+ if (peek(c.io.inp.rd(0).idx.valid) == 1) {
+ expect(c.io.inp.rd(0).idx.bits, inp_indices.dequeue())
+ }
+ if (peek(c.io.wgt.rd(0).idx.valid) == 1) {
+ expect(c.io.wgt.rd(0).idx.bits, wgt_indices.dequeue())
+ }
+ if (peek(c.io.acc.wr(0).valid) == 1) {
+ expect(c.io.acc.wr(0).bits.idx, accout_indices.dequeue())
+ }
+ if (peek(c.io.out.wr(0).valid) == 1) {
+ expect(c.io.out.wr(0).bits.idx, out_indices.dequeue())
+ }
+ }
+
+ def test_if_done() {
+ println(s"uop_indices should be empty ${uop_indices.size}")
+ println(s"acc_indices should be empty ${acc_indices.size}")
+ println(s"inp_indices should be empty ${inp_indices.size}")
+ println(s"wgt_indices should be empty ${wgt_indices.size}")
+ println(s"accout_indices should be empty ${accout_indices.size}")
+ println(s"out_indices should be empty ${out_indices.size}")
+ }
+
+ def check() = {
+ val result = for {
+ ((x,y),idx) <- (acc_scratchpad, acc_o_scratchpad).zipped.toList.zipWithIndex
+ } yield {
+ (for {((xx,yy),jdx) <- (x,y).zipped.toList.zipWithIndex} yield {
+ if (xx != yy) {
+ println(s"Value mismatch at $idx $jdx: $xx (actual) != $yy (expected)")
+ }
+ xx == yy
+ }).reduce((x,y) => x&&y)
+ }
+ val result2 = result.reduce((x,y) => x&&y)
+ result2
+ }
+ }
+
+ val mocks = new Mocks
+
+ for {
+ cnt_o <- BigInt(0) until lp_0
+ cnt_i <- BigInt(0) until lp_1
+ uop_idx <- uop_begin until uop_end
+ } {
+ val u0 = uop_scratchpad(uop_idx.toInt)(0)
+ val u1 = uop_scratchpad(uop_idx.toInt)(1)
+ val u2 = uop_scratchpad(uop_idx.toInt)(2)
+
+ mocks.uop_indices.enqueue(uop_idx)
+ mocks.acc_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+ mocks.inp_indices.enqueue(u1 + inp_0*cnt_o + inp_1*cnt_i)
+ mocks.wgt_indices.enqueue(u2 + wgt_0*cnt_o + wgt_1*cnt_i)
+ mocks.accout_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+
+ if (dec_reset == 0) {
+ mocks.out_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+ }
+ }
+
+ poke(c.io.start, 0)
+ mocks.logical_step()
+ expect(c.io.state, c.sIdle)
+ poke(c.io.start, 1)
+
+ val total_steps = (uop_end-uop_begin)*lp_0*lp_1
+
+ val max_count = 100 + 4*total_steps
+ var count = 0
+ while (peek(c.io.done) == 0 && count < max_count) {
+ if (count % 100 == 0) {
+ println(s"logical_step $count")
+ }
+ mocks.logical_step()
+ if (count == 0) {
+ poke(c.io.start, 0)
+ }
+ count += 1
+ }
+
+ assert(peek(c.io.done) == 1, s"Signal done never high even after $count steps.")
+ println(s"Signal done high after $count steps.")
+
+ mocks.logical_step()
+ expect(c.io.done, 0)
+
+ val cc = mocks.check()
+ println(s"Checking acc with acc_o ${cc}")
+ assert(cc)
+
+ println(s"Total active steps: ${total_steps}")
+ mocks.test_if_done()
+}
+
+class TensorGemmJsonTestx extends GenericTest("TensorGemmJson", (p:Parameters) => new TensorGemmPipelinedSplit()(p),
+ (c:TensorGemmPipelinedSplit) => new TensorGemmJsonTester(c, "/x.json"))
+
+class TensorGemmJsonTesty extends GenericTest("TensorGemmJson", (p:Parameters) => new TensorGemmPipelinedSplit()(p),
+ (c:TensorGemmPipelinedSplit) => new TensorGemmJsonTester(c, "/y.json"))
Review comment:
Thanks for the suggestion, I've renamed both files.
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -272,10 +395,10 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}.elsewhen(!dec.reset) {
when((state === sReadTensor) && mvc.io.acc_o.data.valid) { // issue & commit
}.elsewhen(state === sReadTensor) { // issue a tensor
- assert(inflight =/= ((1<<inflightBits)-1).U)
+ assert( inflight =/= ((1<<inflightBits)-1).U)
Review comment:
Should all be replaced.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] vegaluisjose commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
vegaluisjose commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r655621047
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -272,10 +395,10 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}.elsewhen(!dec.reset) {
when((state === sReadTensor) && mvc.io.acc_o.data.valid) { // issue & commit
}.elsewhen(state === sReadTensor) { // issue a tensor
- assert(inflight =/= ((1<<inflightBits)-1).U)
+ assert( inflight =/= ((1<<inflightBits)-1).U)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
+ state := sIdle
+ io.done := true.B
+ }
+ io.state := state
+
+ assert( state =/= sRun || capture_dec.asUInt === io.dec.asUInt)
+ assert( state =/= sWait || capture_dec.asUInt === io.dec.asUInt)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -368,8 +491,8 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
io.done := done
- if (debug) {
- printf("[TensorGemm] [state]:%d [inflight]:%d\n", state, inflight)
+ if ( debug) {
+ printf( "[TensorGemm] [state]:%d [inflight]:%d\n", state, inflight)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
+ state := sIdle
+ io.done := true.B
+ }
+ io.state := state
+
+ assert( state =/= sRun || capture_dec.asUInt === io.dec.asUInt)
+ assert( state =/= sWait || capture_dec.asUInt === io.dec.asUInt)
+
+ m.io.start := io.start
+
+ m.io.dec := io.dec
+ io.uop.idx.bits := m.io.uop_idx
+ io.uop.idx.valid := m.io.valid
+
+ val delayedUopData = ShiftRegister(io.uop.data, uopReadLatency)
+
+ assert(delayedUopData.valid === delayed_valid)
+
+ val uop_valid = ShiftRegister( delayed_valid, inpReadIdxLatency, resetData = false.B, en = true.B)
+ val uop_acc = ShiftRegister( delayedUopData.bits.u0 + delayed_acc_i, inpReadIdxLatency)
+ val uop_inp = delayedUopData.bits.u1 + delayed_inp_i // it is piped in inp tensor read
+ val uop_wgt = ShiftRegister( delayedUopData.bits.u2 + delayed_wgt_i, inpReadIdxLatency)
+
+ val reset_pipe = Module(
+ new Pipe(
+ Bool(),
+ latency = 3 /* 1 stage is borrowed down here*/ + scratchpadReadLatency + inpReadIdxLatency + uopReadLatency))
+ reset_pipe.io.enq.valid := m.io.valid
+ reset_pipe.io.enq.bits := capture_dec.reset
+
+ val acc_idx_pipe = Module(
+ new Pipe( chiselTypeOf(io.acc.rd(0).idx.bits), latency= 1 /* borrow 1 stage to split*/ + scratchpadReadLatency))
+ acc_idx_pipe.io.enq.valid := uop_valid
+ acc_idx_pipe.io.enq.bits := uop_acc
+
+ require(io.inp.splitWidth == 1 && io.inp.splitLength == 1, "-F- Input split read not supported")
+ io.inp.rd(0).idx.valid := delayed_valid
+ io.inp.rd(0).idx.bits := uop_inp
+ val delayed_uop_valid = RegNext( uop_valid, init=false.B) // memdelay
+ // asset fires on emulated tensorRead Direct GEMM test TODO: fix memoryManager sram read
+ // it works only for VTA_CORE_GEMM_INP_IDX_PIPE 0
+ assert(io.inp.rd(0).data.valid === delayed_uop_valid)
+ for (idx <- 0 until numMVMs) {
+ io.acc.rd(idx).idx.valid := RegNext(acc_idx_pipe.io.deq.valid, init = false.B)
+ io.acc.rd(idx).idx.bits := RegNext(acc_idx_pipe.io.deq.bits)
+
+ // delay wgt read by input result delay latency
+ io.wgt.rd(idx).idx.valid := ShiftRegister(uop_valid, scratchpadReadLatency)
+ io.wgt.rd(idx).idx.bits := ShiftRegister(uop_wgt, scratchpadReadLatency)
+
+ assert(io.wgt.rd(idx).data.valid === ShiftRegister(delayed_uop_valid, scratchpadReadLatency))
+ }
+ io.wgt.tieoffWrite()
+ io.inp.tieoffWrite()
+
+ // create a pipe of 3+ delay with split by goup last stage
+ // and a separate last stage for out and inflight
+ val wrpipe0 = Module( new Pipe(chiselTypeOf(io.acc.wr(0).bits.idx), latency= 2 + scratchpadReadLatency))
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -272,10 +395,10 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}.elsewhen(!dec.reset) {
when((state === sReadTensor) && mvc.io.acc_o.data.valid) { // issue & commit
}.elsewhen(state === sReadTensor) { // issue a tensor
- assert(inflight =/= ((1<<inflightBits)-1).U)
+ assert( inflight =/= ((1<<inflightBits)-1).U)
inflight := inflight + 1.U
}.elsewhen(mvc.io.acc_o.data.valid) { // commit a tensor
- assert(inflight =/= 0.U)
+ assert( inflight =/= 0.U)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
Review comment:
extra space after open paren
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] liangfu commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
liangfu commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r656005109
##########
File path: hardware/chisel/src/test/scala/unittest/TensorGemmTest.scala
##########
@@ -0,0 +1,742 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package unittest
+
+import chisel3._
+import chisel3.util._
+import chisel3.iotesters.PeekPokeTester
+import unittest.util._
+import vta.core._
+import vta.util.config._
+
+class TensorGemmTester(c: TensorGemmOrig) extends PeekPokeTester(c) {
+ poke(c.io.start, 0)
+ poke(c.io.dec.reset, 0)
+ poke(c.io.dec.uop_begin, 0)
+ poke(c.io.dec.uop_end, 1)
+ poke(c.io.dec.lp_0, 1)
+ poke(c.io.dec.lp_1, 1)
+ poke(c.io.dec.acc_0, 1)
+ poke(c.io.dec.acc_1, 1)
+ poke(c.io.dec.inp_0, 1)
+ poke(c.io.dec.inp_1, 1)
+ poke(c.io.dec.wgt_0, 1)
+ poke(c.io.dec.wgt_1, 1)
+ // Don't need empty_0, {push, pop}_{next, prev}, op
+
+ poke(c.io.uop.data.bits.u0, 0)
+ poke(c.io.uop.data.bits.u1, 0)
+ poke(c.io.uop.data.bits.u2, 0)
+
+ val inp = IndexedSeq.fill(c.io.inp.rd(0).data.bits(0).size){BigInt(1)}
+ for {lhs <- c.io.inp.rd(0).data.bits} {
+ poke(lhs, inp.reverse)
+ }
+
+ val wgt = IndexedSeq.fill(c.io.wgt.rd(0).data.bits(0).size){BigInt(1)}
+ for {lhs <- c.io.wgt.rd(0).data.bits} {
+ poke(lhs, wgt.reverse)
+ }
+
+ val acc = IndexedSeq.fill(c.io.acc.rd(0).data.bits(0).size){BigInt(1)}
+ for {lhs <- c.io.acc.rd(0).data.bits} {
+ poke(lhs, acc.reverse)
+ }
+
+ class TensorMasterMock(tm: TensorMaster) {
+ poke(tm.rd(0).data.valid, 0)
+ var valid = peek(tm.rd(0).idx.valid)
+
+ def logical_step(v: BigInt) {
+ poke(tm.rd(0).data.valid, valid)
+ valid = peek(tm.rd(0).idx.valid)
+ expect(tm.rd(0).idx.valid, v)
+ }
+ }
+
+ class UopMasterMock(um: UopMaster) {
+ poke(um.data.valid, 0)
+ var valid = peek(um.idx.valid)
+
+ def logical_step(v: BigInt) {
+ poke(um.data.valid, valid)
+ valid = peek(um.idx.valid)
+ expect(um.idx.valid, v)
+ }
+ }
+
+ class Mocks {
+ val uop_mock = new UopMasterMock(c.io.uop)
+ val inp_mock = new TensorMasterMock(c.io.inp)
+ val wgt_mock = new TensorMasterMock(c.io.wgt)
+ val acc_mock = new TensorMasterMock(c.io.acc)
+
+ def logical_step(sram_valid: BigInt, uop_valid: BigInt) {
+ step(1)
+ uop_mock.logical_step(uop_valid)
+ inp_mock.logical_step(sram_valid)
+ wgt_mock.logical_step(sram_valid)
+ acc_mock.logical_step(sram_valid)
+ }
+ }
+
+ val mocks = new Mocks
+ poke(c.io.start, 0)
+
+ step(1)
+
+ expect(c.io.state, c.sIdle)
+
+ poke(c.io.start, 1)
+ mocks.logical_step(0, 1)
+ expect(c.io.state, c.sReadUop)
+
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+
+ poke(c.io.start, 0)
+
+ mocks.logical_step(0, 0)
+ expect(c.io.state, c.sComputeIdx)
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+
+ mocks.logical_step(1, 0)
+ expect(c.io.state, c.sReadTensor)
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+
+ mocks.logical_step(0, 0)
+ expect(c.io.state, c.sExe)
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+ expect(c.io.done, 0)
+
+ mocks.logical_step(0, 0 /*1*/)
Review comment:
remove unused comment
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -199,7 +322,7 @@ abstract class TensorGemmIfc(implicit p: Parameters) extends Module {
* Also, the TensorGemm uses the reset field in the Gemm instruction to
* clear or zero-out the acc-scratchpad locations based on the micro-ops.
*/
-class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorGemmIfc {
+class TensorGemmOrig(debug: Boolean = false)(implicit p: Parameters) extends TensorGemmIfc {
Review comment:
I think the `Orig` suffix could be better replace with something more meaningful, such as `Simple` or `Small`, indicating the the previous simple design consumes less logic elements, and it's easy to understand.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] aasorokiin edited a comment on pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
aasorokiin edited a comment on pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#issuecomment-866245069
As for logic resource. After synthesis the pipelined VTAShell design has 43 more sequential cells. Pipelined ALU has 6% larger area. Pipelined GEMM has the same area as the Simple one. batch=1, blockin=16, blockout=16
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] adavare commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
adavare commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r655639733
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -272,10 +395,10 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}.elsewhen(!dec.reset) {
when((state === sReadTensor) && mvc.io.acc_o.data.valid) { // issue & commit
}.elsewhen(state === sReadTensor) { // issue a tensor
- assert(inflight =/= ((1<<inflightBits)-1).U)
+ assert( inflight =/= ((1<<inflightBits)-1).U)
Review comment:
Should all be replaced.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] vegaluisjose commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
vegaluisjose commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r655621047
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -272,10 +395,10 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}.elsewhen(!dec.reset) {
when((state === sReadTensor) && mvc.io.acc_o.data.valid) { // issue & commit
}.elsewhen(state === sReadTensor) { // issue a tensor
- assert(inflight =/= ((1<<inflightBits)-1).U)
+ assert( inflight =/= ((1<<inflightBits)-1).U)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
+ state := sIdle
+ io.done := true.B
+ }
+ io.state := state
+
+ assert( state =/= sRun || capture_dec.asUInt === io.dec.asUInt)
+ assert( state =/= sWait || capture_dec.asUInt === io.dec.asUInt)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -368,8 +491,8 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
io.done := done
- if (debug) {
- printf("[TensorGemm] [state]:%d [inflight]:%d\n", state, inflight)
+ if ( debug) {
+ printf( "[TensorGemm] [state]:%d [inflight]:%d\n", state, inflight)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
+ state := sIdle
+ io.done := true.B
+ }
+ io.state := state
+
+ assert( state =/= sRun || capture_dec.asUInt === io.dec.asUInt)
+ assert( state =/= sWait || capture_dec.asUInt === io.dec.asUInt)
+
+ m.io.start := io.start
+
+ m.io.dec := io.dec
+ io.uop.idx.bits := m.io.uop_idx
+ io.uop.idx.valid := m.io.valid
+
+ val delayedUopData = ShiftRegister(io.uop.data, uopReadLatency)
+
+ assert(delayedUopData.valid === delayed_valid)
+
+ val uop_valid = ShiftRegister( delayed_valid, inpReadIdxLatency, resetData = false.B, en = true.B)
+ val uop_acc = ShiftRegister( delayedUopData.bits.u0 + delayed_acc_i, inpReadIdxLatency)
+ val uop_inp = delayedUopData.bits.u1 + delayed_inp_i // it is piped in inp tensor read
+ val uop_wgt = ShiftRegister( delayedUopData.bits.u2 + delayed_wgt_i, inpReadIdxLatency)
+
+ val reset_pipe = Module(
+ new Pipe(
+ Bool(),
+ latency = 3 /* 1 stage is borrowed down here*/ + scratchpadReadLatency + inpReadIdxLatency + uopReadLatency))
+ reset_pipe.io.enq.valid := m.io.valid
+ reset_pipe.io.enq.bits := capture_dec.reset
+
+ val acc_idx_pipe = Module(
+ new Pipe( chiselTypeOf(io.acc.rd(0).idx.bits), latency= 1 /* borrow 1 stage to split*/ + scratchpadReadLatency))
+ acc_idx_pipe.io.enq.valid := uop_valid
+ acc_idx_pipe.io.enq.bits := uop_acc
+
+ require(io.inp.splitWidth == 1 && io.inp.splitLength == 1, "-F- Input split read not supported")
+ io.inp.rd(0).idx.valid := delayed_valid
+ io.inp.rd(0).idx.bits := uop_inp
+ val delayed_uop_valid = RegNext( uop_valid, init=false.B) // memdelay
+ // asset fires on emulated tensorRead Direct GEMM test TODO: fix memoryManager sram read
+ // it works only for VTA_CORE_GEMM_INP_IDX_PIPE 0
+ assert(io.inp.rd(0).data.valid === delayed_uop_valid)
+ for (idx <- 0 until numMVMs) {
+ io.acc.rd(idx).idx.valid := RegNext(acc_idx_pipe.io.deq.valid, init = false.B)
+ io.acc.rd(idx).idx.bits := RegNext(acc_idx_pipe.io.deq.bits)
+
+ // delay wgt read by input result delay latency
+ io.wgt.rd(idx).idx.valid := ShiftRegister(uop_valid, scratchpadReadLatency)
+ io.wgt.rd(idx).idx.bits := ShiftRegister(uop_wgt, scratchpadReadLatency)
+
+ assert(io.wgt.rd(idx).data.valid === ShiftRegister(delayed_uop_valid, scratchpadReadLatency))
+ }
+ io.wgt.tieoffWrite()
+ io.inp.tieoffWrite()
+
+ // create a pipe of 3+ delay with split by goup last stage
+ // and a separate last stage for out and inflight
+ val wrpipe0 = Module( new Pipe(chiselTypeOf(io.acc.wr(0).bits.idx), latency= 2 + scratchpadReadLatency))
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -272,10 +395,10 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}.elsewhen(!dec.reset) {
when((state === sReadTensor) && mvc.io.acc_o.data.valid) { // issue & commit
}.elsewhen(state === sReadTensor) { // issue a tensor
- assert(inflight =/= ((1<<inflightBits)-1).U)
+ assert( inflight =/= ((1<<inflightBits)-1).U)
inflight := inflight + 1.U
}.elsewhen(mvc.io.acc_o.data.valid) { // commit a tensor
- assert(inflight =/= 0.U)
+ assert( inflight =/= 0.U)
Review comment:
extra space after open paren
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,206 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
Review comment:
extra space after open paren
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] adavare commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
adavare commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r656360164
##########
File path: hardware/chisel/src/test/scala/unittest/TensorGemmTest.scala
##########
@@ -0,0 +1,742 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package unittest
+
+import chisel3._
+import chisel3.util._
+import chisel3.iotesters.PeekPokeTester
+import unittest.util._
+import vta.core._
+import vta.util.config._
+
+class TensorGemmTester(c: TensorGemmOrig) extends PeekPokeTester(c) {
+ poke(c.io.start, 0)
+ poke(c.io.dec.reset, 0)
+ poke(c.io.dec.uop_begin, 0)
+ poke(c.io.dec.uop_end, 1)
+ poke(c.io.dec.lp_0, 1)
+ poke(c.io.dec.lp_1, 1)
+ poke(c.io.dec.acc_0, 1)
+ poke(c.io.dec.acc_1, 1)
+ poke(c.io.dec.inp_0, 1)
+ poke(c.io.dec.inp_1, 1)
+ poke(c.io.dec.wgt_0, 1)
+ poke(c.io.dec.wgt_1, 1)
+ // Don't need empty_0, {push, pop}_{next, prev}, op
+
+ poke(c.io.uop.data.bits.u0, 0)
+ poke(c.io.uop.data.bits.u1, 0)
+ poke(c.io.uop.data.bits.u2, 0)
+
+ val inp = IndexedSeq.fill(c.io.inp.rd(0).data.bits(0).size){BigInt(1)}
+ for {lhs <- c.io.inp.rd(0).data.bits} {
+ poke(lhs, inp.reverse)
+ }
+
+ val wgt = IndexedSeq.fill(c.io.wgt.rd(0).data.bits(0).size){BigInt(1)}
+ for {lhs <- c.io.wgt.rd(0).data.bits} {
+ poke(lhs, wgt.reverse)
+ }
+
+ val acc = IndexedSeq.fill(c.io.acc.rd(0).data.bits(0).size){BigInt(1)}
+ for {lhs <- c.io.acc.rd(0).data.bits} {
+ poke(lhs, acc.reverse)
+ }
+
+ class TensorMasterMock(tm: TensorMaster) {
+ poke(tm.rd(0).data.valid, 0)
+ var valid = peek(tm.rd(0).idx.valid)
+
+ def logical_step(v: BigInt) {
+ poke(tm.rd(0).data.valid, valid)
+ valid = peek(tm.rd(0).idx.valid)
+ expect(tm.rd(0).idx.valid, v)
+ }
+ }
+
+ class UopMasterMock(um: UopMaster) {
+ poke(um.data.valid, 0)
+ var valid = peek(um.idx.valid)
+
+ def logical_step(v: BigInt) {
+ poke(um.data.valid, valid)
+ valid = peek(um.idx.valid)
+ expect(um.idx.valid, v)
+ }
+ }
+
+ class Mocks {
+ val uop_mock = new UopMasterMock(c.io.uop)
+ val inp_mock = new TensorMasterMock(c.io.inp)
+ val wgt_mock = new TensorMasterMock(c.io.wgt)
+ val acc_mock = new TensorMasterMock(c.io.acc)
+
+ def logical_step(sram_valid: BigInt, uop_valid: BigInt) {
+ step(1)
+ uop_mock.logical_step(uop_valid)
+ inp_mock.logical_step(sram_valid)
+ wgt_mock.logical_step(sram_valid)
+ acc_mock.logical_step(sram_valid)
+ }
+ }
+
+ val mocks = new Mocks
+ poke(c.io.start, 0)
+
+ step(1)
+
+ expect(c.io.state, c.sIdle)
+
+ poke(c.io.start, 1)
+ mocks.logical_step(0, 1)
+ expect(c.io.state, c.sReadUop)
+
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+
+ poke(c.io.start, 0)
+
+ mocks.logical_step(0, 0)
+ expect(c.io.state, c.sComputeIdx)
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+
+ mocks.logical_step(1, 0)
+ expect(c.io.state, c.sReadTensor)
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+
+ mocks.logical_step(0, 0)
+ expect(c.io.state, c.sExe)
+ expect(c.io.out.wr(0).valid, 0)
+ expect(c.io.acc.wr(0).valid, 0)
+ expect(c.io.done, 0)
+
+ mocks.logical_step(0, 0 /*1*/)
Review comment:
Removed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] adavare commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
adavare commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r656360810
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -199,7 +322,7 @@ abstract class TensorGemmIfc(implicit p: Parameters) extends Module {
* Also, the TensorGemm uses the reset field in the Gemm instruction to
* clear or zero-out the acc-scratchpad locations based on the micro-ops.
*/
-class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorGemmIfc {
+class TensorGemmOrig(debug: Boolean = false)(implicit p: Parameters) extends TensorGemmIfc {
Review comment:
Replaced TensorGemmOrig with TensorGemmSimple
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] adavare commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
adavare commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r654050694
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,218 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
+ state := sIdle
+ io.done := true.B
+ }
+ io.state := state
+
+ if ( false) {
Review comment:
@stevenmburns: Removed, thanks for catching this!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] stevenmburns commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
stevenmburns commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r654043495
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,218 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
+ state := sIdle
+ io.done := true.B
+ }
+ io.state := state
+
+ if ( false) {
Review comment:
@adavare We don't need this anymore.
##########
File path: hardware/chisel/src/main/scala/core/TensorGemm.scala
##########
@@ -421,3 +544,218 @@ class TensorGemm(debug: Boolean = false)(implicit p: Parameters) extends TensorG
}
}
}
+
+class TensorGemmPipelinedSplit (implicit p: Parameters) extends TensorGemmIfc {
+ val sIdle::sRun::sWait::Nil = Enum(3);
+ val numMVMs = p(CoreKey).blockOutFactor
+ val numOuts = p(CoreKey).blockOut / numMVMs
+ require (numOuts > 0, "-F- Cannot factor more groups than blockOut")
+ val batch = p(CoreKey).batch
+
+ val m = Module(new TensorGemmIndexGenerator)
+
+ // additional pipe latency of wgt/inp read if needed
+ val scratchpadReadLatency = 0
+ val inpReadIdxLatency = 0
+ val uopReadLatency = 0
+
+ val delayed_valid = ShiftRegister(m.io.valid, uopReadLatency + 1, resetData = false.B, en = true.B)
+ val delayed_acc_i = ShiftRegister(m.io.acc_i, uopReadLatency + 1)
+ val delayed_inp_i = ShiftRegister(m.io.inp_i, uopReadLatency + 1)
+ val delayed_wgt_i = ShiftRegister(m.io.wgt_i, uopReadLatency + 1)
+
+ val state = RegInit(sIdle)
+ val inflight = RegInit(0.U(inflightBits.W))
+
+ val capture_dec = Reg( chiselTypeOf( io.dec))
+
+ io.done := false.B
+ when( state === sIdle && io.start) {
+ state := sRun
+ capture_dec := io.dec
+ // if (io.dec.empty_0 != None) assert(io.dec.empty_0.get === 0.U)
+ // if (io.dec.empty_1 != None) assert(io.dec.empty_1.get === 0.U)
+ }.elsewhen( state === sRun && m.io.last) {
+ state := sWait
+ }.elsewhen( state === sWait && inflight === 0.U) {
+ state := sIdle
+ io.done := true.B
+ }
+ io.state := state
+
+ if ( false) {
Review comment:
@adavare We don't need this printf debug anymore.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] adavare commented on a change in pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
adavare commented on a change in pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#discussion_r655546750
##########
File path: hardware/chisel/src/test/scala/unittest/TensorGemmJsonTest.scala
##########
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package unittest
+
+import chisel3._
+import chisel3.util._
+import chisel3.iotesters.PeekPokeTester
+import unittest.util._
+import vta.core._
+import vta.util.config._
+
+import scala.io._
+import scala.language.postfixOps
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
+
+class TensorGemmJsonTester(c: TensorGemmPipelinedSplit, fn : String = "/x.json")
+ extends PeekPokeTester(c) {
+
+ val bufferedSource = Source.fromURL(getClass.getResource(fn))
+ val mapper = new ObjectMapper() with ScalaObjectMapper
+ mapper.registerModule(DefaultScalaModule)
+ val archState = mapper.readValue[Map[String, Object]](bufferedSource.reader())
+ bufferedSource.close
+
+ val inst = archState("inst").asInstanceOf[Map[String,String]]
+
+ def build_scratchpad(tag: String) : Array[Array[BigInt]] = {
+ val arr = archState(tag).asInstanceOf[Seq[Map[String,Object]]]
+ (
+ for {(m,i) <- arr zipWithIndex} yield {
+ val idx = BigInt(m("idx").asInstanceOf[String], 16)
+ assert(BigInt(i) == idx)
+ val vec = m("vec").asInstanceOf[Seq[String]]
+ (
+ for {v <- vec} yield {
+ BigInt(v, 16)
+ }
+ ).toArray
+ }
+ ).toArray
+ }
+
+ val inp_scratchpad = build_scratchpad("inp")
+ val wgt_scratchpad = build_scratchpad("wgt")
+ val uop_scratchpad = build_scratchpad("uop")
+ val acc_scratchpad = build_scratchpad("acc_i")
+ val acc_o_scratchpad = build_scratchpad("acc_o")
+
+ poke(c.io.start, 0)
+
+ val dec_reset = BigInt(inst("reset"), 16)
+ val uop_begin = BigInt(inst("uop_begin"), 16)
+ val uop_end = BigInt(inst("uop_end"), 16)
+ assert(uop_begin < uop_end)
+ val lp_0 = BigInt(inst("lp_0"), 16)
+ val lp_1 = BigInt(inst("lp_1"), 16)
+ val acc_0 = BigInt(inst("acc_0"), 16)
+ val inp_0 = BigInt(inst("inp_0"), 16)
+ val wgt_0 = BigInt(inst("wgt_0"), 16)
+ val acc_1 = BigInt(inst("acc_1"), 16)
+ val inp_1 = BigInt(inst("inp_1"), 16)
+ val wgt_1 = BigInt(inst("wgt_1"), 16)
+
+ poke(c.io.dec.reset, dec_reset)
+
+ poke(c.io.dec.uop_begin, uop_begin)
+ poke(c.io.dec.uop_end, uop_end)
+ poke(c.io.dec.lp_0, lp_0)
+ poke(c.io.dec.lp_1, lp_1)
+ poke(c.io.dec.acc_0, acc_0)
+ poke(c.io.dec.acc_1, acc_1)
+ poke(c.io.dec.inp_0, inp_0)
+ poke(c.io.dec.inp_1, inp_1)
+ poke(c.io.dec.wgt_0, wgt_0)
+ poke(c.io.dec.wgt_1, wgt_1)
+ // Don't need empty_0,{push,pop}_{next,prev},op
+
+ class TensorMasterMock(tm: TensorMaster, scratchpad : Array[Array[BigInt]]) {
+ poke(tm.rd(0).data.valid, 0)
+ var valid = peek(tm.rd(0).idx.valid)
+ var idx : Int = 0
+ def logical_step() {
+ if (valid == 1) {
+ poke(tm.rd(0).data.valid, 1)
+ val cols = tm.rd(0).data.bits(0).size
+ for {i <- 0 until tm.rd(0).data.bits.size
+ j <- 0 until cols
+ } {
+ poke(tm.rd(0).data.bits(i)(j), scratchpad(idx)(i*cols + j))
+ }
+ } else {
+ poke(tm.rd(0).data.valid, 0)
+ }
+ valid = peek(tm.rd(0).idx.valid)
+ idx = peek(tm.rd(0).idx.bits).toInt
+ }
+ }
+
+ class TensorMasterMockWr(tm: TensorMaster, scratchpad : Array[Array[BigInt]]) {
+ def logical_step() {
+ if (peek(tm.wr(0).valid) == 1) {
+ val idx = peek(tm.wr(0).bits.idx).toInt
+ val cols = tm.wr(0).bits.data(0).size
+ for {
+ i <- 0 until tm.wr(0).bits.data.size
+ j <- 0 until cols
+ } {
+ scratchpad(idx)(i*cols + j) = peek(tm.wr(0).bits.data(i)(j))
+ }
+ }
+ }
+ }
+
+ class UopMasterMock(um: UopMaster, scratchpad: Array[Array[BigInt]]) {
+ poke(um.data.valid, 0)
+ var valid = peek(um.idx.valid)
+ var idx : Int = 0
+ def logical_step() {
+ if (valid == 1) {
+ poke(um.data.valid, 1)
+ poke(um.data.bits.u0, scratchpad(idx)(0))
+ poke(um.data.bits.u1, scratchpad(idx)(1))
+ poke(um.data.bits.u2, scratchpad(idx)(2))
+ } else {
+ poke(um.data.valid, 0)
+ }
+ valid = peek(um.idx.valid)
+ idx = peek(um.idx.bits).toInt
+ }
+ }
+
+ class Mocks {
+ val uop_mock = new UopMasterMock(c.io.uop, uop_scratchpad)
+ val inp_mock = new TensorMasterMock(c.io.inp, inp_scratchpad)
+ val wgt_mock = new TensorMasterMock(c.io.wgt, wgt_scratchpad)
+ val acc_mock = new TensorMasterMock(c.io.acc, acc_scratchpad)
+ val acc_mock_wr = new TensorMasterMockWr(c.io.acc, acc_scratchpad)
+
+ val uop_indices = new scala.collection.mutable.Queue[BigInt]
+ val acc_indices = new scala.collection.mutable.Queue[BigInt]
+ val inp_indices = new scala.collection.mutable.Queue[BigInt]
+ val wgt_indices = new scala.collection.mutable.Queue[BigInt]
+ val accout_indices = new scala.collection.mutable.Queue[BigInt]
+ val out_indices = new scala.collection.mutable.Queue[BigInt]
+
+ def logical_step() {
+ step(1)
+ uop_mock.logical_step()
+ inp_mock.logical_step()
+ wgt_mock.logical_step()
+ acc_mock.logical_step()
+ acc_mock_wr.logical_step()
+
+ if (peek(c.io.uop.idx.valid) == 1) {
+ expect(c.io.uop.idx.bits, uop_indices.dequeue())
+ }
+ if (peek(c.io.acc.rd(0).idx.valid) == 1) {
+ expect(c.io.acc.rd(0).idx.bits, acc_indices.dequeue())
+ }
+ if (peek(c.io.inp.rd(0).idx.valid) == 1) {
+ expect(c.io.inp.rd(0).idx.bits, inp_indices.dequeue())
+ }
+ if (peek(c.io.wgt.rd(0).idx.valid) == 1) {
+ expect(c.io.wgt.rd(0).idx.bits, wgt_indices.dequeue())
+ }
+ if (peek(c.io.acc.wr(0).valid) == 1) {
+ expect(c.io.acc.wr(0).bits.idx, accout_indices.dequeue())
+ }
+ if (peek(c.io.out.wr(0).valid) == 1) {
+ expect(c.io.out.wr(0).bits.idx, out_indices.dequeue())
+ }
+ }
+
+ def test_if_done() {
+ println(s"uop_indices should be empty ${uop_indices.size}")
+ println(s"acc_indices should be empty ${acc_indices.size}")
+ println(s"inp_indices should be empty ${inp_indices.size}")
+ println(s"wgt_indices should be empty ${wgt_indices.size}")
+ println(s"accout_indices should be empty ${accout_indices.size}")
+ println(s"out_indices should be empty ${out_indices.size}")
+ }
+
+ def check() = {
+ val result = for {
+ ((x,y),idx) <- (acc_scratchpad, acc_o_scratchpad).zipped.toList.zipWithIndex
+ } yield {
+ (for {((xx,yy),jdx) <- (x,y).zipped.toList.zipWithIndex} yield {
+ if (xx != yy) {
+ println(s"Value mismatch at $idx $jdx: $xx (actual) != $yy (expected)")
+ }
+ xx == yy
+ }).reduce((x,y) => x&&y)
+ }
+ val result2 = result.reduce((x,y) => x&&y)
+ result2
+ }
+ }
+
+ val mocks = new Mocks
+
+ for {
+ cnt_o <- BigInt(0) until lp_0
+ cnt_i <- BigInt(0) until lp_1
+ uop_idx <- uop_begin until uop_end
+ } {
+ val u0 = uop_scratchpad(uop_idx.toInt)(0)
+ val u1 = uop_scratchpad(uop_idx.toInt)(1)
+ val u2 = uop_scratchpad(uop_idx.toInt)(2)
+
+ mocks.uop_indices.enqueue(uop_idx)
+ mocks.acc_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+ mocks.inp_indices.enqueue(u1 + inp_0*cnt_o + inp_1*cnt_i)
+ mocks.wgt_indices.enqueue(u2 + wgt_0*cnt_o + wgt_1*cnt_i)
+ mocks.accout_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+
+ if (dec_reset == 0) {
+ mocks.out_indices.enqueue(u0 + acc_0*cnt_o + acc_1*cnt_i)
+ }
+ }
+
+ poke(c.io.start, 0)
+ mocks.logical_step()
+ expect(c.io.state, c.sIdle)
+ poke(c.io.start, 1)
+
+ val total_steps = (uop_end-uop_begin)*lp_0*lp_1
+
+ val max_count = 100 + 4*total_steps
+ var count = 0
+ while (peek(c.io.done) == 0 && count < max_count) {
+ if (count % 100 == 0) {
+ println(s"logical_step $count")
+ }
+ mocks.logical_step()
+ if (count == 0) {
+ poke(c.io.start, 0)
+ }
+ count += 1
+ }
+
+ assert(peek(c.io.done) == 1, s"Signal done never high even after $count steps.")
+ println(s"Signal done high after $count steps.")
+
+ mocks.logical_step()
+ expect(c.io.done, 0)
+
+ val cc = mocks.check()
+ println(s"Checking acc with acc_o ${cc}")
+ assert(cc)
+
+ println(s"Total active steps: ${total_steps}")
+ mocks.test_if_done()
+}
+
+class TensorGemmJsonTestx extends GenericTest("TensorGemmJson", (p:Parameters) => new TensorGemmPipelinedSplit()(p),
+ (c:TensorGemmPipelinedSplit) => new TensorGemmJsonTester(c, "/x.json"))
+
+class TensorGemmJsonTesty extends GenericTest("TensorGemmJson", (p:Parameters) => new TensorGemmPipelinedSplit()(p),
+ (c:TensorGemmPipelinedSplit) => new TensorGemmJsonTester(c, "/y.json"))
Review comment:
Thanks for the suggestion, I've renamed both files.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] liangfu merged pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
liangfu merged pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [tvm-vta] aasorokiin commented on pull request #30: Chisel Pipelined GEMM
Posted by GitBox <gi...@apache.org>.
aasorokiin commented on pull request #30:
URL: https://github.com/apache/tvm-vta/pull/30#issuecomment-866245069
As for logic resource. After synthesis the pipelined VTAShell design has 43 more sequential cells. Pipelined ALU has 6% larger area. Pipelined GEMM has the same area as the Simple one.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org