Initial commit.
[sifive-blocks.git] / src / main / scala / devices / spi / SPIFlash.scala
1 // See LICENSE for license details.
2 package sifive.blocks.devices.spi
3
4 import Chisel._
5
6 class SPIFlashInsn(c: SPIFlashConfigBase) extends SPIBundle(c) {
7 val cmd = new Bundle with HasSPIProtocol {
8 val code = Bits(width = c.insnCmdBits)
9 val en = Bool()
10 }
11 val addr = new Bundle with HasSPIProtocol {
12 val len = UInt(width = c.insnAddrLenBits)
13 }
14 val pad = new Bundle {
15 val code = Bits(width = c.frameBits)
16 val cnt = Bits(width = c.insnPadLenBits)
17 }
18 val data = new Bundle with HasSPIProtocol
19 }
20
21 class SPIFlashControl(c: SPIFlashConfigBase) extends SPIBundle(c) {
22 val insn = new SPIFlashInsn(c)
23 val fmt = new Bundle with HasSPIEndian
24 }
25
26 object SPIFlashInsn {
27 def init(c: SPIFlashConfigBase): SPIFlashInsn = {
28 val insn = Wire(new SPIFlashInsn(c))
29 insn.cmd.en := Bool(true)
30 insn.cmd.code := Bits(0x03)
31 insn.cmd.proto := SPIProtocol.Single
32 insn.addr.len := UInt(3)
33 insn.addr.proto := SPIProtocol.Single
34 insn.pad.cnt := UInt(0)
35 insn.pad.code := Bits(0)
36 insn.data.proto := SPIProtocol.Single
37 insn
38 }
39 }
40
41 class SPIFlashAddr(c: SPIFlashConfigBase) extends SPIBundle(c) {
42 val next = UInt(width = c.insnAddrBits)
43 val hold = UInt(width = c.insnAddrBits)
44 }
45
46 class SPIFlashMap(c: SPIFlashConfigBase) extends Module {
47 val io = new Bundle {
48 val en = Bool(INPUT)
49 val ctrl = new SPIFlashControl(c).asInput
50 val addr = Decoupled(new SPIFlashAddr(c)).flip
51 val data = Decoupled(UInt(width = c.frameBits))
52 val link = new SPIInnerIO(c)
53 }
54
55 val addr = io.addr.bits.hold + UInt(1)
56 val merge = io.link.active && (io.addr.bits.next === addr)
57
58 private val insn = io.ctrl.insn
59 io.link.tx.valid := Bool(true)
60 io.link.fmt.proto := insn.addr.proto
61 io.link.fmt.iodir := SPIDirection.Tx
62 io.link.fmt.endian := io.ctrl.fmt.endian
63 io.link.cnt := Mux1H(
64 SPIProtocol.decode(io.link.fmt.proto).zipWithIndex.map {
65 case (s, i) => (s -> UInt(c.frameBits >> i))
66 })
67 io.link.cs.set := Bool(true)
68 io.link.cs.clear := Bool(false)
69 io.link.cs.hold := Bool(true)
70 io.link.lock := Bool(true)
71
72 io.addr.ready := Bool(false)
73 io.data.valid := Bool(false)
74 io.data.bits := io.link.rx.bits
75
76 val cnt = Reg(UInt(width = math.max(c.insnPadLenBits, c.insnAddrLenBits)))
77 val cnt_en = Wire(init = Bool(false))
78 val cnt_cmp = (0 to c.insnAddrBytes).map(cnt === UInt(_))
79 val cnt_zero = cnt_cmp(0)
80 val cnt_last = cnt_cmp(1) && io.link.tx.ready
81 val cnt_done = cnt_last || cnt_zero
82 when (cnt_en) {
83 io.link.tx.valid := !cnt_zero
84 when (io.link.tx.fire()) {
85 cnt := cnt - UInt(1)
86 }
87 }
88
89 val (s_idle :: s_cmd :: s_addr :: s_pad :: s_data_pre :: s_data_post :: Nil) = Enum(UInt(), 6)
90 val state = Reg(init = s_idle)
91
92 switch (state) {
93 is (s_idle) {
94 io.link.tx.valid := Bool(false)
95 when (io.en) {
96 io.addr.ready := Bool(true)
97 when (io.addr.valid) {
98 when (merge) {
99 state := s_data_pre
100 } .otherwise {
101 state := Mux(insn.cmd.en, s_cmd, s_addr)
102 io.link.cs.clear := Bool(true)
103 }
104 } .otherwise {
105 io.link.lock := Bool(false)
106 }
107 } .otherwise {
108 io.data.valid := io.addr.valid
109 io.addr.ready := io.data.ready
110 io.data.bits := UInt(0)
111 io.link.lock := Bool(false)
112 }
113 }
114
115 is (s_cmd) {
116 io.link.fmt.proto := insn.cmd.proto
117 io.link.tx.bits := insn.cmd.code
118 when (io.link.tx.ready) {
119 state := s_addr
120 cnt := insn.addr.len
121 }
122 }
123
124 is (s_addr) {
125 io.link.tx.bits := Mux1H(cnt_cmp.tail.zipWithIndex.map {
126 case (s, i) =>
127 val n = i * c.frameBits
128 val m = n + (c.frameBits - 1)
129 s -> io.addr.bits.hold(m, n)
130 })
131
132 cnt_en := Bool(true)
133 when (cnt_done) {
134 state := s_pad
135 }
136 }
137
138 is (s_pad) {
139 io.link.cnt := insn.pad.cnt
140 io.link.tx.bits := insn.pad.code
141 when (io.link.tx.ready) {
142 state := s_data_pre
143 }
144 }
145
146 is (s_data_pre) {
147 io.link.fmt.proto := insn.data.proto
148 io.link.fmt.iodir := SPIDirection.Rx
149 when (io.link.tx.ready) {
150 state := s_data_post
151 }
152 }
153
154 is (s_data_post) {
155 io.link.tx.valid := Bool(false)
156 io.data.valid := io.link.rx.valid
157 when (io.data.fire()) {
158 state := s_idle
159 }
160 }
161 }
162 }