Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

asmicon: multiplexer (untested)

  • Loading branch information...
commit c26efa28ca22c69a1947bf0e2ef69869e8f259e3 1 parent 0e00837
Sébastien Bourdeauducq authored March 18, 2012
9  milkymist/asmicon/__init__.py
@@ -20,12 +20,19 @@ def __init__(self, bank_a, row_a, col_a):
20 20
 		self.mux_a = max(row_a, col_a)
21 21
 
22 22
 class TimingSettings:
23  
-	def __init__(self, tRP, tRCD, tREFI, tRFC, slot_time):
  23
+	def __init__(self, tRP, tRCD, tWR, tREFI, tRFC, CL, rd_delay, slot_time, read_time, write_time):
24 24
 		self.tRP = tRP
25 25
 		self.tRCD = tRCD
  26
+		self.tWR = tWR
26 27
 		self.tREFI = tREFI
27 28
 		self.tRFC = tRFC
  29
+		
  30
+		self.CL = CL
  31
+		self.rd_delay = rd_delay
  32
+		
28 33
 		self.slot_time = slot_time
  34
+		self.read_time = read_time
  35
+		self.write_time = write_time
29 36
 
30 37
 class ASMIcon:
31 38
 	def __init__(self, phy_settings, geom_settings, timing_settings):
36  milkymist/asmicon/bankmachine.py
@@ -150,7 +150,7 @@ def __init__(self, geom_settings, timing_settings, address_align, bankn, slots):
150 150
 		
151 151
 		self.refresh_req = Signal()
152 152
 		self.refresh_gnt = Signal()
153  
-		self.cmd_request = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a,
  153
+		self.cmd = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a,
154 154
 			bits_for(len(slots)-1))
155 155
 
156 156
 	def get_fragment(self):
@@ -182,15 +182,15 @@ def get_fragment(self):
182 182
 		# Address generation
183 183
 		s_row_adr = Signal()
184 184
 		comb += [
185  
-			self.cmd_request.ba.eq(self.bankn),
  185
+			self.cmd.ba.eq(self.bankn),
186 186
 			If(s_row_adr,
187  
-				self.cmd_request.a.eq(slicer.row(buf.adr))
  187
+				self.cmd.a.eq(slicer.row(buf.adr))
188 188
 			).Else(
189  
-				self.cmd_request.a.eq(slicer.col(buf.adr))
  189
+				self.cmd.a.eq(slicer.col(buf.adr))
190 190
 			)
191 191
 		]
192 192
 		
193  
-		comb.append(self.cmd_request.tag.eq(buf.tag))
  193
+		comb.append(self.cmd.tag.eq(buf.tag))
194 194
 		
195 195
 		# Control and command generation FSM
196 196
 		fsm = FSM("REGULAR", "PRECHARGE", "ACTIVATE", "REFRESH", delayed_enters=[
@@ -203,12 +203,12 @@ def get_fragment(self):
203 203
 			).Elif(buf.stb,
204 204
 				If(has_openrow,
205 205
 					If(hit,
206  
-						self.cmd_request.stb.eq(1),
207  
-						buf.ack.eq(self.cmd_request.ack),
208  
-						self.cmd_request.is_read.eq(~buf.we),
209  
-						self.cmd_request.is_write.eq(buf.we),
210  
-						self.cmd_request.cas_n.eq(0),
211  
-						self.cmd_request.we_n.eq(~buf.we)
  206
+						self.cmd.stb.eq(1),
  207
+						buf.ack.eq(self.cmd.ack),
  208
+						self.cmd.is_read.eq(~buf.we),
  209
+						self.cmd.is_write.eq(buf.we),
  210
+						self.cmd.cas_n.eq(0),
  211
+						self.cmd.we_n.eq(~buf.we)
212 212
 					).Else(
213 213
 						fsm.next_state(fsm.PRECHARGE)
214 214
 					)
@@ -222,17 +222,17 @@ def get_fragment(self):
222 222
 			# 1. we are presenting the column address, A10 is always low
223 223
 			# 2. since we always go to the ACTIVATE state, we do not need
224 224
 			# to assert track_close.
225  
-			self.cmd_request.stb.eq(1),
226  
-			If(self.cmd_request.ack, fsm.next_state(fsm.TRP)),
227  
-			self.cmd_request.ras_n.eq(0),
228  
-			self.cmd_request.we_n.eq(0)
  225
+			self.cmd.stb.eq(1),
  226
+			If(self.cmd.ack, fsm.next_state(fsm.TRP)),
  227
+			self.cmd.ras_n.eq(0),
  228
+			self.cmd.we_n.eq(0)
229 229
 		)
230 230
 		fsm.act(fsm.ACTIVATE,
231 231
 			s_row_adr.eq(1),
232 232
 			track_open.eq(1),
233  
-			self.cmd_request.stb.eq(1),
234  
-			If(self.cmd_request.ack, fsm.next_state(fsm.TRCD)),
235  
-			self.cmd_request.ras_n.eq(0)
  233
+			self.cmd.stb.eq(1),
  234
+			If(self.cmd.ack, fsm.next_state(fsm.TRCD)),
  235
+			self.cmd.ras_n.eq(0)
236 236
 		)
237 237
 		fsm.act(fsm.REFRESH,
238 238
 			self.refresh_gnt.eq(1),
261  milkymist/asmicon/multiplexer.py
... ...
@@ -1,4 +1,9 @@
  1
+import math
  2
+
1 3
 from migen.fhdl.structure import *
  4
+from migen.corelogic.roundrobin import *
  5
+from migen.corelogic.misc import multimux, optree
  6
+from migen.corelogic.fsm import FSM
2 7
 
3 8
 class CommandRequest:
4 9
 	def __init__(self, a, ba):
@@ -17,9 +22,261 @@ def __init__(self, a, ba, tagbits):
17 22
 		self.is_write = Signal()
18 23
 		self.tag = Signal(BV(tagbits))
19 24
 
  25
+class _CommandChooser:
  26
+	def __init__(self, requests, tagbits):
  27
+		self.requests = requests
  28
+		
  29
+		self.want_reads = Signal()
  30
+		self.want_writes = Signal()
  31
+		# NB: cas_n/ras_n/we_n are 1 when stb is inactive
  32
+		self.cmd = CommandRequestRW(self.requests[0].a.bv.width, self.requests[0].ba.bv.width, tagbits)
  33
+	
  34
+	def get_fragment(self):
  35
+		comb = []
  36
+		sync = []
  37
+		
  38
+		rr = RoundRobin(len(self.requests), SP_CE)
  39
+		
  40
+		comb += [rr.request[i].eq(req.stb & ((req.is_read == self.want_reads) | (req.is_write == self.want_writes)))
  41
+			for i, req in enumerate(self.requests)]
  42
+		
  43
+		stb = Signal()
  44
+		inputs_perm = [[req.stb,
  45
+			req.a, req.ba,
  46
+			req.is_read, req.is_write, req.tag] for req in self.requests]
  47
+		outputs_perm = [stb,
  48
+			self.cmd.a, self.cmd.ba,
  49
+			self.cmd.is_read, self.cmd.is_write, self.cmd.tag]
  50
+		comb += multimux(rr.grant, inputs_perm, outputs_perm)
  51
+		
  52
+		inputs_filtered = [[req.cas_n, req.ras_n, req.we_n] for req in self.requests]
  53
+		outputs_filtered = [self.cmd.cas_n, self.cmd.ras_n, self.cmd.we_n]
  54
+		ms = multimux(rr.grant, inputs_filtered, outputs_filtered)
  55
+		comb += [
  56
+			self.cmd.stb.eq(stb & ((self.cmd.is_read == self.want_reads) | (self.cmd.is_write == self.want_writes))),
  57
+			If(self.cmd.stb, *ms)
  58
+		]
  59
+		
  60
+		comb += [req.ack.eq(self.cmd.stb & self.cmd.ack & rr.grant == i)
  61
+			for i, req in enumerate(self.requests)]
  62
+		comb.append(rr.ce.eq(self.cmd.ack))
  63
+		
  64
+		return Fragment(comb, sync) + rr.get_fragment()
  65
+
  66
+class _Steerer:
  67
+	def __init__(self, commands, dfi):
  68
+		self.commands = commands
  69
+		self.dfi = dfi
  70
+		
  71
+		ncmd = len(self.commands)
  72
+		nph = len(self.dfi.phases)
  73
+		self.sel = [Signal(BV(bits_for(ncmd-1))) for i in range(nph)]
  74
+	
  75
+	def get_fragment(self):
  76
+		comb = []
  77
+		sync = []
  78
+		def stb_and(cmd, attr):
  79
+			if not hasattr(cmd, "stb"):
  80
+				return Constant(0)
  81
+			else:
  82
+				return cmd.stb & getattr(cmd, attr)
  83
+		inputs = [[cmd.a, cmd.ba,
  84
+			cmd.cas_n, cmd.ras_n,
  85
+			cmd.we_n, stb_and(cmd, "is_read"), stb_and(cmd, "is_write")]
  86
+			for cmd in self.commands]
  87
+		for phase, sel in zip(self.dfi.phases, self.sel):
  88
+			comb += [
  89
+				phase.cke.eq(1),
  90
+				phase.cs_n.eq(0)
  91
+			]
  92
+			outputs = [phase.address, phase.bank,
  93
+				phase.cas_n, phase.ras_n, phase.we_n,
  94
+				phase.rddata_en, phase.wrdata_en]
  95
+			sync += multimux(sel, inputs, outputs)
  96
+		return Fragment(comb, sync)
  97
+
  98
+class _Datapath:
  99
+	def __init__(self, timing_settings, command, dfi, hub):
  100
+		self.timing_settings = timing_settings
  101
+		self.command = command
  102
+		self.dfi = dfi
  103
+		self.hub = hub
  104
+	
  105
+	def get_fragment(self):
  106
+		comb = []
  107
+		sync = []
  108
+		tagbits = self.hub.tag_call.bv.width
  109
+		
  110
+		rd_valid = Signal()
  111
+		rd_tag = Signal(BV(tagbits))
  112
+		wr_valid = Signal()
  113
+		wr_tag = Signal(BV(tagbits))
  114
+		comb += [
  115
+			self.hub.call.eq(rd_valid | wr_valid),
  116
+			If(wr_valid,
  117
+				self.hub.tag_call.eq(wr_tag)
  118
+			).Else(
  119
+				self.hub.tag_call.eq(rd_tag)
  120
+			)
  121
+		]
  122
+		
  123
+		rd_valid_d = [Signal() for i in range(self.timing_settings.rd_delay)]
  124
+		rd_tag_d = [Signal(BV(tagbits)) for i in range(self.timing_settings.rd_delay)]
  125
+		for i in range(self.timing_settings.rd_delay):
  126
+			if i:
  127
+				sync += [
  128
+					rd_valid_d[i].eq(rd_valid_d[i-1]),
  129
+					rd_tag_d[i].eq(rd_tag_d[i-1])
  130
+				]
  131
+			else:
  132
+				sync += [
  133
+					rd_valid_d[i].eq(self.command.stb & self.command.ack & self.command.is_read),
  134
+					rd_tag_d[i].eq(self.command.tag)
  135
+				]		
  136
+		comb += [
  137
+			rd_valid.eq(rd_valid_d[-1]),
  138
+			rd_tag.eq(rd_tag_d[-1]),
  139
+			wr_valid.eq(self.command.stb & self.command.ack & self.command.is_write),
  140
+			wr_tag.eq(self.command.tag),
  141
+		]
  142
+		
  143
+		all_rddata = [p.rddata for p in self.dfi.phases]
  144
+		all_wrdata = [p.wrdata for p in self.dfi.phases]
  145
+		all_wrdata_mask = [p.wrdata_mask for p in self.dfi.phases]
  146
+		comb += [
  147
+			self.hub.dat_r.eq(Cat(*all_rddata)),
  148
+			Cat(*all_wrdata).eq(self.hub.dat_w),
  149
+			Cat(*all_wrdata_mask).eq(self.hub.dat_wm)
  150
+		]
  151
+		
  152
+		return Fragment(comb, sync)
  153
+
20 154
 class Multiplexer:
21 155
 	def __init__(self, phy_settings, geom_settings, timing_settings, bank_machines, refresher, dfi, hub):
22  
-		pass
  156
+		self.phy_settings = phy_settings
  157
+		self.geom_settings = geom_settings
  158
+		self.timing_settings = timing_settings
  159
+		self.bank_machines = bank_machines
  160
+		self.refresher = refresher
  161
+		self.dfi = dfi
  162
+		self.hub = hub
  163
+		
  164
+		assert(self.phy_settings.nphases == len(dfi.phases))
  165
+		if self.phy_settings.nphases != 2:
  166
+			raise NotImplementedError("TODO: multiplexer only supports 2 phases")
23 167
 	
24 168
 	def get_fragment(self):
25  
-		return Fragment()
  169
+		comb = []
  170
+		sync = []
  171
+		
  172
+		# Command choosing
  173
+		requests = [bm.cmd for bm in self.bank_machines]
  174
+		tagbits = self.hub.tag_call.bv.width
  175
+		choose_cmd = _CommandChooser(requests, tagbits)
  176
+		choose_req = _CommandChooser(requests, tagbits)
  177
+		comb += [
  178
+			choose_cmd.want_reads.eq(0),
  179
+			choose_cmd.want_writes.eq(0)
  180
+		]
  181
+		
  182
+		# Command steering
  183
+		nop = CommandRequest(self.geom_settings.mux_a, self.geom_settings.bank_a)
  184
+		commands = [nop, choose_cmd.cmd, choose_req.cmd, self.refresher.cmd] # nop must be 1st
  185
+		(STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4)
  186
+		steerer = _Steerer(commands, self.dfi)
  187
+		
  188
+		# Read/write turnaround
  189
+		read_available = Signal()
  190
+		write_available = Signal()
  191
+		comb += [
  192
+			read_available.eq(optree("|", [req.stb & req.is_read for req in requests])),
  193
+			write_available.eq(optree("|", [req.stb & req.is_write for req in requests]))
  194
+		]
  195
+		
  196
+		def anti_starvation(timeout):
  197
+			en = Signal()
  198
+			max_time = Signal()
  199
+			if timeout:
  200
+				t = timeout - 1
  201
+				time = Signal(BV(bits_for(t)))
  202
+				comb.append(max_time.eq(time == 0))
  203
+				sync.append(
  204
+					If(~en,
  205
+						time.eq(t)
  206
+					).Elif(~max_time,
  207
+						time.eq(time - 1)
  208
+					)
  209
+				)
  210
+			else:
  211
+				comb.append(max_time.eq(0))
  212
+			return en, max_time
  213
+		read_time_en, max_read_time = anti_starvation(self.timing_settings.read_time)
  214
+		write_time_en, max_write_time = anti_starvation(self.timing_settings.write_time)
  215
+		
  216
+		# Refresh
  217
+		refresh_w_ok = Signal()
  218
+		t_unsafe_refresh = 2 + self.timing_settings.tWR - 1
  219
+		unsafe_refresh_count = Signal(BV(bits_for(t_unsafe_refresh)))
  220
+		comb.append(refresh_w_ok.eq(unsafe_refresh_count == 0))
  221
+		sync += [
  222
+			If(choose_req.cmd.stb & choose_req.cmd.ack & choose_req.cmd.is_write,
  223
+				unsafe_refresh_count.eq(t_unsafe_refresh)
  224
+			).Elif(~refresh_w_ok,
  225
+				unsafe_refresh_count.eq(unsafe_refresh_count-1)
  226
+			)
  227
+		]
  228
+		# Reads cannot conflict with refreshes, since we have one idle cycle
  229
+		# (all bank machines in refresh state) before the PRECHARGE ALL command
  230
+		# from the refresher.
  231
+		comb += [bm.refresh_req.eq(self.refresher.req)
  232
+			for bm in self.bank_machines]
  233
+		comb.append(
  234
+			self.refresher.ack.eq(optree("&",
  235
+				[bm.refresh_gnt for bm in self.bank_machines]) \
  236
+				& refresh_w_ok)
  237
+		)
  238
+		
  239
+		# Datapath
  240
+		datapath = _Datapath(self.timing_settings, choose_req.cmd, self.dfi, self.hub)
  241
+		
  242
+		# Control FSM
  243
+		fsm = FSM("READ", "WRITE", "REFRESH", delayed_enters=[
  244
+			("RTW", "WRITE", math.ceil((self.timing_settings.CL+1)/2)),
  245
+			("WTR", "READ", self.timing_settings.tWR)
  246
+		])
  247
+		fsm.act(fsm.READ,
  248
+			read_time_en.eq(1),
  249
+			choose_req.want_reads.eq(1),
  250
+			choose_cmd.cmd.ack.eq(1),
  251
+			choose_req.cmd.ack.eq(1),
  252
+			steerer.sel[1-self.phy_settings.rdphase].eq(STEER_CMD),
  253
+			steerer.sel[self.phy_settings.rdphase].eq(STEER_REQ),
  254
+			If(write_available,
  255
+				# TODO: switch only after several cycles of ~read_available?
  256
+				If(~read_available | max_read_time, fsm.next_state(fsm.RTW))
  257
+			),
  258
+			If(self.refresher.ack, fsm.next_state(fsm.REFRESH))
  259
+		)
  260
+		fsm.act(fsm.WRITE,
  261
+			write_time_en.eq(1),
  262
+			choose_req.want_writes.eq(1),
  263
+			choose_cmd.cmd.ack.eq(1),
  264
+			choose_req.cmd.ack.eq(1),
  265
+			steerer.sel[1-self.phy_settings.wrphase].eq(STEER_CMD),
  266
+			steerer.sel[self.phy_settings.wrphase].eq(STEER_REQ),
  267
+			If(read_available,
  268
+				If(~write_available | max_write_time, fsm.next_state(fsm.WTR))
  269
+			),
  270
+			If(self.refresher.ack, fsm.next_state(fsm.REFRESH))
  271
+		)
  272
+		fsm.act(fsm.REFRESH,
  273
+			steerer.sel[0].eq(STEER_REFRESH),
  274
+			If(~self.refresher.req, fsm.next_state(fsm.READ))
  275
+		)
  276
+		
  277
+		return Fragment(comb, sync) + \
  278
+			choose_cmd.get_fragment() + \
  279
+			choose_req.get_fragment() + \
  280
+			steerer.get_fragment() + \
  281
+			datapath.get_fragment() + \
  282
+			fsm.get_fragment()
28  milkymist/asmicon/refresher.py
@@ -11,8 +11,8 @@ def __init__(self, a, ba, tRP, tREFI, tRFC):
11 11
 		self.tRFC = tRFC
12 12
 		
13 13
 		self.req = Signal()
14  
-		self.ack = Signal()
15  
-		self.cmd_request = CommandRequest(a, ba)
  14
+		self.ack = Signal() # 1st command 1 cycle after assertion of ack
  15
+		self.cmd = CommandRequest(a, ba)
16 16
 	
17 17
 	def get_fragment(self):
18 18
 		comb = []
@@ -23,22 +23,22 @@ def get_fragment(self):
23 23
 		seq_start = Signal()
24 24
 		seq_done = Signal()
25 25
 		sync += [
26  
-			self.cmd_request.a.eq(2**10),
27  
-			self.cmd_request.ba.eq(0),
28  
-			self.cmd_request.cas_n.eq(1),
29  
-			self.cmd_request.ras_n.eq(1),
30  
-			self.cmd_request.we_n.eq(1)
  26
+			self.cmd.a.eq(2**10),
  27
+			self.cmd.ba.eq(0),
  28
+			self.cmd.cas_n.eq(1),
  29
+			self.cmd.ras_n.eq(1),
  30
+			self.cmd.we_n.eq(1)
31 31
 		]
32 32
 		sync += timeline(seq_start, [
33  
-			(0, [
34  
-				self.cmd_request.ras_n.eq(0),
35  
-				self.cmd_request.we_n.eq(0)
  33
+			(1, [
  34
+				self.cmd.ras_n.eq(0),
  35
+				self.cmd.we_n.eq(0)
36 36
 			]),
37  
-			(self.tRP, [
38  
-				self.cmd_request.cas_n.eq(0),
39  
-				self.cmd_request.ras_n.eq(0)
  37
+			(1+self.tRP, [
  38
+				self.cmd.cas_n.eq(0),
  39
+				self.cmd.ras_n.eq(0)
40 40
 			]),
41  
-			(self.tRP+self.tRFC-1, [
  41
+			(1+self.tRP+self.tRFC, [
42 42
 				seq_done.eq(1)
43 43
 			])
44 44
 		])
9  top.py
@@ -33,9 +33,16 @@ def ns(t, margin=True):
33 33
 sdram_timing = asmicon.TimingSettings(
34 34
 	tRP=ns(15),
35 35
 	tRCD=ns(15),
  36
+	tWR=ns(15),
36 37
 	tREFI=ns(7800, False),
37 38
 	tRFC=ns(70),
38  
-	slot_time=16
  39
+	
  40
+	CL=3,
  41
+	rd_delay=4,
  42
+
  43
+	slot_time=16,
  44
+	read_time=32,
  45
+	write_time=16
39 46
 )
40 47
 
41 48
 def ddrphy_clocking(crg, phy):

0 notes on commit c26efa2

Please sign in to comment.
Something went wrong with that request. Please try again.