aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/main/scala/vexriscv/ip/fpu/FpuDiv.scala
blob: 7c9e713ec78ac63b2d84a01d4ea036a424213bfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package vexriscv.ip.fpu


import spinal.core._
import spinal.lib.math.{UnsignedDividerCmd, UnsignedDividerRsp}
import spinal.lib._
import spinal.lib.sim.{StreamDriver, StreamMonitor, StreamReadyRandomizer}

import scala.collection.mutable
import scala.util.Random

case class FpuDivCmd(mantissaWidth : Int) extends Bundle{
  val a,b = UInt(mantissaWidth bits)
}

case class FpuDivRsp(mantissaWidth : Int) extends Bundle{
  val result = UInt(mantissaWidth+1 + 2 bits)
  val remain = UInt(mantissaWidth+1 bits)
}

case class FpuDiv(val mantissaWidth : Int) extends Component {
  assert(mantissaWidth % 2 == 0)
  val io = new Bundle{
    val input = slave Stream(FpuDivCmd(mantissaWidth))
    val output = master Stream(FpuDivRsp(mantissaWidth))
  }

  val iterations = (mantissaWidth+2+2)/2
  val counter = Reg(UInt(log2Up(iterations) bits))
  val busy = RegInit(False) clearWhen(io.output.fire)
  val done = RegInit(False) setWhen(busy && counter === iterations-1) clearWhen(io.output.fire)

  val shifter = Reg(UInt(mantissaWidth + 3 bits))
  val result = Reg(UInt(mantissaWidth+1+2 bits))

  val div1, div3 = Reg(UInt(mantissaWidth+3 bits))
  val div2 = div1 |<< 1

  val sub1 = shifter -^ div1
  val sub2 = shifter -^ div2
  val sub3 = shifter -^ div3

  io.output.valid := done
  io.output.result := (result << 0).resized
  io.output.remain := (shifter >> 2).resized
  io.input.ready := !busy

  when(!done){
    counter := counter + 1
    val sel = CombInit(shifter)
    result := result |<< 2
    when(!sub1.msb){
      sel := sub1.resized
      result(1 downto 0) := 1
    }
    when(!sub2.msb){
      sel := sub2.resized
      result(1 downto 0) := 2
    }
    when(!sub3.msb){
      sel := sub3.resized
      result(1 downto 0) := 3
    }
    shifter := sel |<< 2
  }

  when(!busy){
    counter := 0
    shifter := (U"1" @@ io.input.a @@ U"").resized
    div1    := (U"1" @@ io.input.b).resized
    div3    := (U"1" @@ io.input.b) +^ (((U"1" @@ io.input.b)) << 1)
    busy := io.input.valid
  }
}


object FpuDivTester extends App{
  import spinal.core.sim._

  for(w <- List(16, 20)) {
    val config = SimConfig
    config.withFstWave
    config.compile(new FpuDiv(w)).doSim(seed=2){dut =>
      dut.clockDomain.forkStimulus(10)


      val (cmdDriver, cmdQueue) = StreamDriver.queue(dut.io.input, dut.clockDomain)
      val rspQueue = mutable.Queue[FpuDivRsp => Unit]()
      StreamMonitor(dut.io.output, dut.clockDomain)( rspQueue.dequeue()(_))
      StreamReadyRandomizer(dut.io.output, dut.clockDomain)

      def test(a : Int, b : Int): Unit ={
        cmdQueue +={p =>
          p.a #= a
          p.b #= b
        }
        rspQueue += {p =>
          val x = (a | (1 << dut.mantissaWidth)).toLong
          val y = (b | (1 << dut.mantissaWidth)).toLong
          val result = (x << dut.mantissaWidth+2) / y
          val remain = (x << dut.mantissaWidth+2) % y

          assert(p.result.toLong == result, f"$x%x/$y%x=${p.result.toLong}%x instead of $result%x")
          assert(p.remain.toLong == remain, f"$x%x %% $y%x=${p.remain.toLong}%x instead of $remain%x")
        }
      }

      val s = dut.mantissaWidth-16
      val f = (1 << dut.mantissaWidth)-1
      test(0xE000 << s, 0x8000 << s)
      test(0xC000 << s, 0x4000 << s)
      test(0xC835 << s, 0x4742 << s)
      test(0,0)
      test(0,f)
      test(f,0)
      test(f,f)

      for(i <- 0 until 10000){
        test(Random.nextInt(1 << dut.mantissaWidth), Random.nextInt(1 << dut.mantissaWidth))
      }

      waitUntil(rspQueue.isEmpty)

      dut.clockDomain.waitSampling(100)

    }
  }
}

object FpuDivTester2 extends App{
  val mantissaWidth = 52
  val a = BigInt(0xfffffff810000l)
  val b = BigInt(0x0000000000FF0l)
  val x = (a | (1l << mantissaWidth))
  val y = (b | (1l << mantissaWidth))
  val result = (x << mantissaWidth+2) / y
  val remain = (x << mantissaWidth+2) % y
  println("done")

}