From e464935119abf590157fd1770518c26e71c98921 Mon Sep 17 00:00:00 2001
From: Sebastien Bourdeauducq <sebastien@milkymist.org>
Date: Tue, 21 Jan 2014 15:56:51 +0100
Subject: [PATCH] downscaler: add chopper module

---
 misoclib/videostream/downscaler.py | 135 +++++++++++++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 misoclib/videostream/downscaler.py

diff --git a/misoclib/videostream/downscaler.py b/misoclib/videostream/downscaler.py
new file mode 100644
index 00000000..cda3b9bb
--- /dev/null
+++ b/misoclib/videostream/downscaler.py
@@ -0,0 +1,135 @@
+from migen.fhdl.std import *
+from migen.genlib.fsm import *
+
+class Chopper(Module):
+	def __init__(self, N, frac_bits):
+		self.init = Signal()
+		self.ready = Signal()
+		self.next = Signal()
+		self.p = Signal(frac_bits)
+		self.q = Signal(frac_bits)
+		self.chopper = Signal(N)
+
+		###
+
+		# initialization counter
+		ic = Signal(frac_bits)
+		ic_overflow = Signal()
+		ic_inc = Signal()
+		self.sync += \
+			If(self.init,
+				ic.eq(0),
+				ic_overflow.eq(1)
+			).Elif(ic_inc,
+				If(ic + self.p >= self.q,
+					ic.eq(ic + self.p - self.q),
+					ic_overflow.eq(1)
+				).Else(
+					ic.eq(ic + self.p),
+					ic_overflow.eq(0)
+				)
+			)
+
+		# computed N*p mod q
+		Np = Signal(frac_bits)
+		load_np = Signal()
+		self.sync += If(load_np, Np.eq(ic))
+
+		fsm = FSM()
+		self.submodules += fsm
+		fsm.act("IDLE",
+			self.ready.eq(1),
+			If(self.init, NextState(0))
+		)
+
+		prev_acc_r = Signal(frac_bits)
+		prev_acc = prev_acc_r
+		for i in range(N):
+			acc = Signal(frac_bits)
+
+			# pipeline stage 1: update accumulators
+			load_init_acc = Signal()
+			self.sync += \
+				If(load_init_acc,
+					acc.eq(ic)
+				).Elif(self.next,
+					If(acc + Np >= Cat(self.q, 0), # FIXME: workaround for imbecilic Verilog extension rules, needs to be put in Migen backend
+						acc.eq(acc + Np - self.q),
+					).Else(
+						acc.eq(acc + Np)
+					)
+				)
+
+			# pipeline stage 2: detect overflows and generate chopper signal
+			load_init_chopper = Signal()
+			self.sync += \
+				If(load_init_chopper,
+					self.chopper[i].eq(ic_overflow)
+				).Elif(self.next,
+					self.chopper[i].eq(prev_acc >= acc)
+				)
+			if i == N-1:
+				self.sync += \
+					If(load_init_chopper,
+						prev_acc_r.eq(ic)	
+					).Elif(self.next,
+						prev_acc_r.eq(acc)
+					)
+			prev_acc = acc
+
+			# initialize stage 2
+			fsm.act(i, 
+				load_init_chopper.eq(1),
+				ic_inc.eq(1),
+				NextState(i + 1)
+			)
+			# initialize stage 1
+			fsm.act(N + i,
+				load_init_acc.eq(1),
+				ic_inc.eq(1),
+				NextState(N + i + 1) if i < N-1 else NextState("IDLE")
+			)
+		# initialize Np
+		fsm.act(N, load_np.eq(1))
+
+def _count_ones(n):
+	r = 0
+	while n:
+		if n & 1:
+			r += 1
+		n >>= 1
+	return r
+
+class _ChopperTB(Module):
+	def __init__(self):
+		self.submodules.dut = Chopper(4, 16)
+
+	def gen_simulation(self, s):
+		from migen.sim.generic import Proxy
+		dut = Proxy(s, self.dut)
+
+		dut.init = 1
+		dut.p = 320
+		dut.q = 681
+		yield
+		dut.init = 0
+		yield
+		while not dut.ready:
+			print("waiting")
+			yield
+		print("done")
+
+		dut.next = 1
+		yield
+		ones = 0
+		niter = 681
+		for i in range(niter):
+			print("{:04b}".format(dut.chopper))
+			ones += _count_ones(dut.chopper)
+			yield
+		print("Ones: {} (expected: {})".format(ones, dut.p*niter*4//dut.q))
+
+if __name__ == "__main__":
+	from migen.sim.generic import Simulator
+	with Simulator(_ChopperTB()) as s:
+		s.run(1000)
-- 
2.30.2