Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 3 commits
  • 2 files changed
  • 0 comments
  • 2 contributors
Jun 26, 2013
Robert Jordens migen/genlib/cordic.py: generic cordic
* rotating or vectoring cordic modes
* circular, linear, or hyperbolic functions
* combinatorial, pipelined or iterative evaluation
* arbitrary width, stages and guard bits
* two or four quadrant mode for circular/rotate
0224ea0
Sébastien Bourdeauducq fhdl/verilog: fix signedness rules for comparison 080afdc
Sébastien Bourdeauducq genlib/cordic: cleanup 48a5b86
2  migen/fhdl/verilog.py
@@ -51,7 +51,7 @@ def _printexpr(ns, node):
51 51
 				s = s1
52 52
 		elif arity == 2:
53 53
 			r2, s2 = _printexpr(ns, node.operands[1])
54  
-			if node.op in ["+", "-", "*", "&", "^", "|"]:
  54
+			if node.op not in ["<<<", ">>>"]:
55 55
 				if s2 and not s1:
56 56
 					r1 = "$signed({1'd0, " + r1 + "})"
57 57
 				if s1 and not s2:
241  migen/genlib/cordic.py
... ...
@@ -0,0 +1,241 @@
  1
+from math import atan, atanh, log, sqrt, pi
  2
+
  3
+from migen.fhdl.std import *
  4
+
  5
+class TwoQuadrantCordic(Module):
  6
+	"""
  7
+	http://eprints.soton.ac.uk/267873/1/tcas1_cordic_review.pdf
  8
+	"""
  9
+	def __init__(self, width=16, stages=None, guard=0,
  10
+			eval_mode="iterative", cordic_mode="rotate",
  11
+			func_mode="circular"):
  12
+		# validate parameters
  13
+		assert eval_mode in ("combinatorial", "pipelined", "iterative")
  14
+		assert cordic_mode in ("rotate", "vector")
  15
+		self.cordic_mode = cordic_mode
  16
+		assert func_mode in ("circular", "linear", "hyperbolic")
  17
+		self.func_mode = func_mode
  18
+		if guard is None:
  19
+			# guard bits to guarantee "width" accuracy
  20
+			guard = int(log(width)/log(2))
  21
+		if stages is None:
  22
+			stages = width + guard
  23
+
  24
+		# calculate the constants
  25
+		if func_mode == "circular":
  26
+			s = range(stages)
  27
+			a = [atan(2**-i) for i in s]
  28
+			g = [sqrt(1 + 2**(-2*i)) for i in s]
  29
+			zmax = pi/2
  30
+		elif func_mode == "linear":
  31
+			s = range(stages)
  32
+			a = [2**-i for i in s]
  33
+			g = [1 for i in s]
  34
+			zmax = 2.
  35
+		elif func_mode == "hyperbolic":
  36
+			s = list(range(1, stages+1))
  37
+			# need to repeat these stages:
  38
+			j = 4
  39
+			while j < stages+1:
  40
+				s.append(j)
  41
+				j = 3*j + 1
  42
+			s.sort()
  43
+			stages = len(s)
  44
+			a = [atanh(2**-i) for i in s]
  45
+			g = [sqrt(1 - 2**(-2*i)) for i in s]
  46
+			zmax = 1.
  47
+
  48
+		a = [Signal((width+guard, True), "{}{}".format("a", i),
  49
+			reset=int(round(ai*2**(width + guard - 1)/zmax)))
  50
+			for i, ai in enumerate(a)]
  51
+		self.zmax = zmax #/2**(width - 1)
  52
+		self.gain = 1.
  53
+		for gi in g:
  54
+			self.gain *= gi
  55
+
  56
+		exec_target, num_reg, self.latency, self.interval = {
  57
+			"combinatorial": (self.comb, stages + 1, 0,          1),
  58
+			"pipelined":     (self.sync, stages + 1, stages,     1),
  59
+			"iterative":     (self.sync, 3,          stages + 1, stages + 1),
  60
+			}[eval_mode]
  61
+
  62
+		# i/o and inter-stage signals
  63
+		self.fresh = Signal()
  64
+		self.xi, self.yi, self.zi, self.xo, self.yo, self.zo = (
  65
+				Signal((width, True), l + io) for io in "io" for l in "xyz")
  66
+		x, y, z = ([Signal((width + guard, True), "{}{}".format(l, i))
  67
+			for i in range(num_reg)] for l in "xyz")
  68
+
  69
+		self.comb += [
  70
+			x[0].eq(self.xi<<guard),
  71
+			y[0].eq(self.yi<<guard),
  72
+			z[0].eq(self.zi<<guard),
  73
+			self.xo.eq(x[-1]>>guard),
  74
+			self.yo.eq(y[-1]>>guard),
  75
+			self.zo.eq(z[-1]>>guard),
  76
+			]
  77
+
  78
+		if eval_mode in ("combinatorial", "pipelined"):
  79
+			self.comb += self.fresh.eq(1)
  80
+			for i in range(stages):
  81
+				exec_target += self.stage(x[i], y[i], z[i],
  82
+						x[i + 1], y[i + 1], z[i + 1], i, a[i])
  83
+		elif eval_mode == "iterative":
  84
+			# we afford one additional iteration for register in/out
  85
+			# shifting, trades muxes for registers
  86
+			i = Signal(max=stages + 1)
  87
+			ai = Signal((width+guard, True))
  88
+			self.comb += ai.eq(Array(a)[i])
  89
+			exec_target += [
  90
+					i.eq(i + 1),
  91
+					If(i == stages,
  92
+						i.eq(0),
  93
+						self.fresh.eq(1),
  94
+						Cat(x[1], y[1], z[1]).eq(Cat(x[0], y[0], z[0])),
  95
+						Cat(x[2], y[2], z[2]).eq(Cat(x[1], y[1], z[1])),
  96
+					).Else(
  97
+						self.fresh.eq(0),
  98
+						# in-place stages
  99
+						self.stage(x[1], y[1], z[1], x[1], y[1], z[1], i, ai),
  100
+					)]
  101
+
  102
+	def stage(self, xi, yi, zi, xo, yo, zo, i, a):
  103
+		"""
  104
+		x_{i+1} = x_{i} - m*d_i*y_i*r**(-s_{m,i})
  105
+		y_{i+1} = d_i*x_i*r**(-s_{m,i}) + y_i
  106
+		z_{i+1} = z_i - d_i*a_{m,i}
  107
+
  108
+		d_i: clockwise or counterclockwise
  109
+		r: radix of the number system
  110
+		m: 1: circular, 0: linear, -1: hyperbolic
  111
+		s_{m,i}: non decreasing integer shift sequence
  112
+		a_{m,i}: elemetary rotation angle
  113
+		"""
  114
+		dx, dy, dz = xi>>i, yi>>i, a
  115
+		direction = {"rotate": zi < 0, "vector": yi >= 0}[self.cordic_mode]
  116
+		dy = {"circular": dy, "linear": 0, "hyperbolic": -dy}[self.func_mode]
  117
+		ret = If(direction,
  118
+					xo.eq(xi + dy),
  119
+					yo.eq(yi - dx),
  120
+					zo.eq(zi + dz),
  121
+				).Else(
  122
+					xo.eq(xi - dy),
  123
+					yo.eq(yi + dx),
  124
+					zo.eq(zi - dz),
  125
+				)
  126
+		return ret
  127
+
  128
+class Cordic(TwoQuadrantCordic):
  129
+	def __init__(self, **kwargs):
  130
+		TwoQuadrantCordic.__init__(self, **kwargs)
  131
+		if not (self.func_mode, self.cordic_mode) == ("circular", "rotate"):
  132
+			return # no need to remap quadrants
  133
+		cxi, cyi, czi, cxo, cyo, czo = (self.xi, self.yi, self.zi,
  134
+				self.xo, self.yo, self.zo)
  135
+		width = flen(self.xi)
  136
+		for l in "xyz":
  137
+			for d in "io":
  138
+				setattr(self, l+d, Signal((width, True), l+d))
  139
+		qin = Signal()
  140
+		qout = Signal()
  141
+		if self.latency == 0:
  142
+			self.comb += qout.eq(qin)
  143
+		elif self.latency == 1:
  144
+			self.sync += qout.eq(qin)
  145
+		else:
  146
+			sr = Signal(self.latency-1)
  147
+			self.sync += Cat(sr, qout).eq(Cat(qin, sr))
  148
+		pi2 = (1<<(width-2))-1
  149
+		self.zmax *= 2
  150
+		self.comb += [
  151
+				# zi, zo are scaled to cover the range, this also takes
  152
+				# care of mapping the zi quadrants
  153
+				Cat(cxi, cyi, czi).eq(Cat(self.xi, self.yi, self.zi<<1)),
  154
+				Cat(self.xo, self.yo, self.zo).eq(Cat(cxo, cyo, czo>>1)),
  155
+				# shift in the (2,3)-quadrant flag
  156
+				qin.eq((-self.zi < -pi2) | (self.zi+1 < -pi2)),
  157
+				# need to remap xo/yo quadrants (2,3) -> (4,1)
  158
+				If(qout,
  159
+					self.xo.eq(-cxo),
  160
+					self.yo.eq(-cyo),
  161
+				)]
  162
+
  163
+class TB(Module):
  164
+	def __init__(self, n, **kwargs):
  165
+		self.submodules.cordic = Cordic(**kwargs)
  166
+		self.xi = [.9/self.cordic.gain] * n
  167
+		self.yi = [0] * n
  168
+		self.zi = [2*i/n-1 for i in range(n)]
  169
+		self.xo = []
  170
+		self.yo = []
  171
+		self.zo = []
  172
+
  173
+	def do_simulation(self, s):
  174
+		c = 2**(flen(self.cordic.xi)-1)
  175
+		if s.rd(self.cordic.fresh):
  176
+			self.xo.append(s.rd(self.cordic.xo))
  177
+			self.yo.append(s.rd(self.cordic.yo))
  178
+			self.zo.append(s.rd(self.cordic.zo))
  179
+			if not self.xi:
  180
+				s.interrupt = True
  181
+				return
  182
+			for r, v in zip((self.cordic.xi, self.cordic.yi, self.cordic.zi),
  183
+					(self.xi, self.yi, self.zi)):
  184
+				s.wr(r, int(v.pop(0)*c))
  185
+
  186
+def _main():
  187
+	from migen.fhdl import verilog
  188
+	from migen.sim.generic import Simulator, TopLevel
  189
+	from matplotlib import pyplot as plt
  190
+	import numpy as np
  191
+
  192
+	c = Cordic(width=16, eval_mode="iterative",
  193
+		cordic_mode="rotate", func_mode="circular")
  194
+	print(verilog.convert(c, ios={c.xi, c.yi, c.zi, c.xo,
  195
+		c.yo, c.zo}))
  196
+
  197
+	n = 200
  198
+	tb = TB(n, width=8, guard=3, eval_mode="pipelined",
  199
+			cordic_mode="rotate", func_mode="circular")
  200
+	sim = Simulator(tb, TopLevel("cordic.vcd"))
  201
+	sim.run(n*16+20)
  202
+	plt.plot(tb.xo)
  203
+	plt.plot(tb.yo)
  204
+	plt.plot(tb.zo)
  205
+	plt.show()
  206
+
  207
+def _rms_err(width, stages, n):
  208
+	from migen.sim.generic import Simulator
  209
+	import numpy as np
  210
+	import matplotlib.pyplot as plt
  211
+
  212
+	tb = TB(width=int(width), stages=int(stages), n=n,
  213
+			eval_mode="combinatorial")
  214
+	sim = Simulator(tb)
  215
+	sim.run(n+100)
  216
+	z = tb.cordic.zmax*(np.arange(n)/n*2-1)
  217
+	x = np.cos(z)*.9
  218
+	y = np.sin(z)*.9
  219
+	dx = tb.xo[1:]-x*2**(width-1)
  220
+	dy = tb.yo[1:]-y*2**(width-1)
  221
+	return ((dx**2+dy**2)**.5).sum()/n
  222
+
  223
+def _test_err():
  224
+	from matplotlib import pyplot as plt
  225
+	import numpy as np
  226
+
  227
+	widths, stages = np.mgrid[4:33:1, 4:33:1]
  228
+	err = np.vectorize(lambda w, s: rms_err(w, s, 173))(widths, stages)
  229
+	err = -np.log2(err)/widths
  230
+	print(err)
  231
+	plt.contour(widths, stages, err, 50, cmap=plt.cm.Greys)
  232
+	plt.plot(widths[:, 0], stages[0, np.argmax(err, 1)], "bx-")
  233
+	print(widths[:, 0], stages[0, np.argmax(err, 1)])
  234
+	plt.colorbar()
  235
+	plt.grid("on")
  236
+	plt.show()
  237
+
  238
+if __name__ == "__main__":
  239
+	_main()
  240
+	#_rms_err(16, 16, 345)
  241
+	#_test_err()

No commit comments for this range

Something went wrong with that request. Please try again.