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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
use crate::core::prelude::*;
use crate::hls::bridge::Bridge;
use crate::hls::bus::{SoCBusResponder, SoCPortController};
use crate::hls::prelude::{MISOWidePort, MOSIPort, MOSIWidePort};
use crate::hls::HLSNamedPorts;
use crate::widgets::prelude::*;
#[derive(Debug, Copy, Clone, LogicState, PartialEq)]
enum State {
Boot,
Idle,
Writing,
Reading,
}
#[derive(LogicBlock)]
pub struct SDRAMControllerTester<const R: usize, const C: usize> {
pub dram: SDRAMDriver<16>,
pub upstream: SoCBusResponder<16, 8>,
local_bridge: Bridge<16, 8, 5>,
count: MOSIWidePort<32, 16>,
cmd: MOSIPort<16>,
write_out: MISOWidePort<32, 16>,
error_out: MISOWidePort<32, 16>,
validation_out: MISOWidePort<32, 16>,
controller: SDRAMBaseController<R, C, 64, 16>,
lsfr: LFSRSimple,
entropy_funnel: CrossWidenFIFO<32, 6, 7, 64, 3, 4>,
output_funnel: CrossNarrowFIFO<64, 3, 4, 32, 6, 7>,
lsfr_validate: LFSRSimple,
dram_address: DFF<Bits<32>>,
error_count: DFF<Bits<32>>,
validation_count: DFF<Bits<32>>,
write_count: DFF<Bits<32>>,
output_pipeline: DFF<Bits<32>>,
output_avail: DFF<Bit>,
state: DFF<State>,
}
impl<const R: usize, const C: usize> SDRAMControllerTester<R, C> {
pub fn new(cas_delay: u32, timings: MemoryTimings, buffer: OutputBuffer) -> Self {
Self {
dram: Default::default(),
upstream: Default::default(),
local_bridge: Bridge::new(["count", "cmd", "errors", "valid", "write"]),
count: Default::default(),
cmd: Default::default(),
write_out: Default::default(),
error_out: Default::default(),
validation_out: Default::default(),
controller: SDRAMBaseController::new(cas_delay, timings, buffer),
lsfr: Default::default(),
entropy_funnel: CrossWidenFIFO::new(WordOrder::LeastSignificantFirst),
output_funnel: CrossNarrowFIFO::new(WordOrder::LeastSignificantFirst),
lsfr_validate: Default::default(),
dram_address: Default::default(),
error_count: Default::default(),
validation_count: Default::default(),
write_count: Default::default(),
output_pipeline: Default::default(),
output_avail: Default::default(),
state: Default::default(),
}
}
}
impl<const R: usize, const C: usize> HLSNamedPorts for SDRAMControllerTester<R, C> {
fn ports(&self) -> Vec<String> {
self.local_bridge.ports()
}
}
impl<const R: usize, const C: usize> Logic for SDRAMControllerTester<R, C> {
#[hdl_gen]
fn update(&mut self) {
SoCBusResponder::<16, 8>::link(&mut self.upstream, &mut self.local_bridge.upstream);
SDRAMDriver::<16>::link(&mut self.dram, &mut self.controller.sdram);
self.controller.clock.next = self.upstream.clock.val();
SoCPortController::<16>::join(&mut self.local_bridge.nodes[0], &mut self.count.bus);
SoCPortController::<16>::join(&mut self.local_bridge.nodes[1], &mut self.cmd.bus);
SoCPortController::<16>::join(&mut self.local_bridge.nodes[2], &mut self.error_out.bus);
SoCPortController::<16>::join(
&mut self.local_bridge.nodes[3],
&mut self.validation_out.bus,
);
SoCPortController::<16>::join(&mut self.local_bridge.nodes[4], &mut self.write_out.bus);
self.state.clk.next = self.upstream.clock.val();
self.dram_address.clk.next = self.upstream.clock.val();
self.lsfr_validate.clock.next = self.upstream.clock.val();
self.validation_count.clk.next = self.upstream.clock.val();
self.validation_count.d.next = self.validation_count.q.val();
self.write_count.clk.next = self.upstream.clock.val();
self.write_count.d.next = self.write_count.q.val();
self.error_count.clk.next = self.upstream.clock.val();
self.error_count.d.next = self.error_count.q.val();
self.cmd.ready.next = false;
self.state.d.next = self.state.q.val();
self.dram_address.d.next = self.dram_address.q.val();
self.controller.data_in.next = self.entropy_funnel.data_out.val();
self.entropy_funnel.data_in.next = self.lsfr.num.val();
self.lsfr.strobe.next =
!self.entropy_funnel.full.val() & (self.state.q.val() != State::Boot);
self.lsfr.clock.next = self.upstream.clock.val();
self.entropy_funnel.write.next =
!self.entropy_funnel.full.val() & (self.state.q.val() != State::Boot);
self.entropy_funnel.write_clock.next = self.upstream.clock.val();
self.entropy_funnel.read_clock.next = self.upstream.clock.val();
self.controller.data_in.next = self.entropy_funnel.data_out.val();
self.controller.cmd_address.next = self.dram_address.q.val();
self.controller.write_not_read.next = false;
self.controller.cmd_strobe.next = false;
self.entropy_funnel.read.next = false;
self.output_funnel.data_in.next = self.controller.data_out.val();
self.output_funnel.write.next = self.controller.data_valid.val();
self.output_funnel.write_clock.next = self.upstream.clock.val();
self.output_funnel.read_clock.next = self.upstream.clock.val();
self.error_out.strobe_in.next = false;
self.validation_out.strobe_in.next = false;
self.write_out.strobe_in.next = false;
self.lsfr.reset.next = false;
self.lsfr_validate.reset.next = false;
match self.state.q.val() {
State::Boot => {
self.lsfr.reset.next = true;
self.lsfr_validate.reset.next = true;
self.state.d.next = State::Idle;
}
State::Idle => {
self.cmd.ready.next = true;
if self.cmd.strobe_out.val() {
self.error_count.d.next = 0_usize.into();
self.dram_address.d.next = 0_usize.into();
self.state.d.next = State::Writing;
self.validation_count.d.next = 0_usize.into();
self.write_count.d.next = 0_usize.into();
}
}
State::Writing => {
if self.write_count.q.val() >= self.count.port_out.val() {
if !self.controller.busy.val() {
self.dram_address.d.next = 0_usize.into();
self.state.d.next = State::Reading;
self.write_out.strobe_in.next = true;
}
} else if !self.controller.busy.val() & !self.entropy_funnel.empty.val() {
self.controller.write_not_read.next = true;
self.controller.cmd_strobe.next = true;
self.dram_address.d.next = self.dram_address.q.val() + 4_usize;
self.entropy_funnel.read.next = true;
self.write_count.d.next = self.write_count.q.val() + 4_usize;
}
}
State::Reading => {
if self.dram_address.q.val() >= self.count.port_out.val() {
if self.validation_count.q.val() >= self.count.port_out.val() {
self.state.d.next = State::Idle;
self.error_out.strobe_in.next = true;
self.validation_out.strobe_in.next = true;
}
} else if !self.controller.busy.val() & !self.output_funnel.full.val() {
self.controller.write_not_read.next = false;
self.controller.cmd_strobe.next = true;
self.dram_address.d.next = self.dram_address.q.val() + 4_usize;
}
}
}
self.output_avail.clk.next = self.upstream.clock.val();
self.output_avail.d.next = self.output_avail.q.val();
self.output_pipeline.clk.next = self.upstream.clock.val();
self.output_pipeline.d.next = self.output_pipeline.q.val();
self.output_funnel.read.next = false;
if !self.output_avail.q.val() & !self.output_funnel.empty.val() {
self.output_pipeline.d.next = self.output_funnel.data_out.val();
self.output_funnel.read.next = true;
self.output_avail.d.next = true;
}
self.lsfr_validate.strobe.next = false;
if self.output_avail.q.val() {
if self.output_pipeline.q.val() != self.lsfr_validate.num.val() {
self.error_count.d.next = self.error_count.q.val() + 2_usize;
}
self.output_avail.d.next = false;
self.lsfr_validate.strobe.next = true;
self.validation_count.d.next = self.validation_count.q.val() + 2_usize;
}
self.error_out.port_in.next = self.error_count.q.val();
self.validation_out.port_in.next = self.validation_count.q.val();
self.write_out.port_in.next = self.write_count.q.val();
}
}
#[test]
fn test_sdram_controller_tester_synthesizes() {
let mut uut = SDRAMControllerTester::<6, 4>::new(
3,
MemoryTimings::fast_boot_sim(100e6),
OutputBuffer::DelayOne,
);
uut.dram.read_data.connect();
uut.upstream.link_connect_dest();
uut.connect_all();
yosys_validate("sdram_controller_tester_hls", &generate_verilog(&uut)).unwrap();
}