Skip to content

Commit

Permalink
hdl/csc/rgb2ycbcr: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
enjoy-digital committed Aug 10, 2015
1 parent 1546b10 commit c099049
Show file tree
Hide file tree
Showing 4 changed files with 389 additions and 18 deletions.
63 changes: 63 additions & 0 deletions hdl/csc/common.py
@@ -0,0 +1,63 @@
from migen.fhdl.std import *

def saturate(i, o, minimum, maximum):
return [
If(i > maximum,
o.eq(maximum)
).Elif(i < minimum,
o.eq(minimum)
).Else(
o.eq(i)
)
]

def coef(value, cw=None):
return int(value * 2**cw) if cw is not None else value

def sd_ntsc_coefs(dw, cw=None):
return {
"ca" : coef(0.2568, cw),
"cb" : coef(0.0979, cw),
"cc" : coef(0.5910, cw),
"cd" : coef(0.5772, cw),
"yoffset" : 2**(dw-4),
"coffset" : 2**(dw-1),
"ymax" : 235*2**(dw-8),
"cmax" : 235*2**(dw-8),
"ymin" : 2**dw-1,
"cmin" : 2**dw-1
}

def hd_pal_coefs(dw, cw=None):
return {
"ca" : coef(0.1819, cw),
"cb" : coef(0.0618, cw),
"cc" : coef(0.6495, cw),
"cd" : coef(0.5512, cw),
"yoffset" : 2**(dw-4),
"coffset" : 2**(dw-1),
"ymax" : 2**dw-1,
"cmax" : 2**dw-1,
"ymin" : 0,
"cmin" : 0
}

def yuv_coefs(dw, cw=None):
return {
"ca" : coef(0.299, cw),
"cb" : coef(0.114, cw),
"cc" : coef(0.877283, cw),
"cd" : coef(0.492111, cw),
"yoffset" : 2**(dw-4),
"coffset" : 2**(dw-1),
"ymax" : 2**(dw)-1,
"cmax" : 2**(dw)-1,
"ymin" : 0,
"cmin" : 0
}

def ycbcr_layout(dw):
return [("y", dw), ("cb", dw), ("cr", dw)]

def rgb_layout(dw):
return [("r", dw), ("g", dw), ("b", dw)]
136 changes: 136 additions & 0 deletions hdl/csc/rgb2ycbcr.py
@@ -0,0 +1,136 @@
# rgb2ycbcr
# (XAPP930 migen implementation)

from migen.fhdl.std import *
from migen.genlib.record import *
from migen.flow.actor import *

from hdl.csc.common import *


datapath_latency = 7


@DecorateModule(InsertCE)
class RGB2YCbCrDatapath(Module):
def __init__(self, rgb_w, ycbcr_w, coef_w, coefs):
self.sink = sink = Record(rgb_layout(rgb_w))
self.source = source = Record(ycbcr_layout(ycbcr_w))

# # #

# delay rgb signals
rgb_delayed = [Sink(rgb_layout(rgb_w))]
for i in range(datapath_latency):
rgb_n = Record(rgb_layout(rgb_w))
for name in ["r", "g", "b"]:
self.comb += getattr(rgb_n, name).eq(getattr(rgb_delayed[-1], name))
rgb_delayed.append(rgb_n)

# Hardware implementation:
# y = ca*(r-g) + g + cb*(b-g) + yoffset
# cb = cc*(r-y) + coffset
# cr = cd*(b-y) + coffset

# clk 0
# (r-g) & (b-g)
r_minus_g = Signal((rgb_w + 1, True))
b_minus_g = Signal((rgb_w + 1, True))
self.sync += [
r_minus_g.eq(sink.r - sink.g),
b_minus_g.eq(sink.b - sink.g)
]

# clk 1
# ca*(r-g) & cb*(b-g)
ca_mult_rg = Signal((rgb_w + coef_w + 1, True))
cb_mult_bg = Signal((rgb_w + coef_w + 1, True))
self.sync += [
ca_mult_rg.eq(r_minus_g * coefs["ca"]),
cb_mult_bg.eq(b_minus_g * coefs["cb"])
]

# clk 2
# ca*(r-g) + cb*(b-g)
carg_plus_cbbg = Signal((rgb_w + coef_w + 2, True))
self.sync += [
carg_plus_cbbg.eq(ca_mult_rg + cb_mult_bg)
]

# clk 3
# yraw = ca*(r-g) + cb*(b-g) + g
yraw = Signal((rgb_w + 3, True))
self.sync += [
yraw.eq(carg_plus_cbbg[coef_w:] + rgb_delayed[2].g)
]

# clk 4
# r - yraw
# b - yraw
b_minus_yraw = Signal((rgb_w + 4, True))
r_minus_yraw = Signal((rgb_w + 4, True))
yraw_r0 = Signal((rgb_w + 3, True))
self.sync += [
b_minus_yraw.eq(rgb_delayed[3].b - yraw),
r_minus_yraw.eq(rgb_delayed[3].r - yraw),
yraw_r0.eq(yraw)
]

# clk 5
# cc*yraw
# cd*yraw
cc_mult_ryraw = Signal((rgb_w + coef_w + 4, True))
cd_mult_byraw = Signal((rgb_w + coef_w + 4, True))
yraw_r1 = Signal((rgb_w + 3, True))
self.sync += [
cc_mult_ryraw.eq(b_minus_yraw * coefs["cc"]),
cd_mult_byraw.eq(r_minus_yraw * coefs["cd"]),
yraw_r1.eq(yraw_r0)
]

# clk 6
# y = (yraw + yoffset)
# cb = (cc*(r - yraw) + coffset)
# cr = (cd*(b - yraw) + coffset)
y = Signal((rgb_w + 3, True))
cb = Signal((rgb_w + 4, True))
cr = Signal((rgb_w + 4, True))
self.sync += [
y.eq(yraw_r1 + coefs["yoffset"]),
cb.eq(cc_mult_ryraw[coef_w:] + coefs["coffset"]),
cr.eq(cd_mult_byraw[coef_w:] + coefs["coffset"])
]

# clk 7
# saturate
self.sync += [
saturate(y, source.y, coefs["ymin"], coefs["ymax"]),
saturate(cb, source.cb, coefs["cmin"], coefs["cmax"]),
saturate(cr, source.cr, coefs["cmin"], coefs["cmax"])
]


class RGB2YCbCr(PipelinedActor, Module):
def __init__(self, rgb_w=8, ycbcr_w=8, coef_w=8, mode="HD"):
self.sink = sink = Sink(rgb_layout(rgb_w))
self.source = source = Source(ycbcr_layout(ycbcr_w))
PipelinedActor.__init__(self, datapath_latency)

# # #

if mode in ["SD", "NTSC"]:
coefs = sd_ntsc_coefs(ycbcr_w, coef_w)
elif mode in ["HD", "PAL"]:
coefs = hd_pal_coefs(ycbcr_w, coef_w)
elif mode in ["YUV"]:
coefs = yuv_coefs(ycbcr_w, coef_w)
else:
ValueError

# datapath
self.submodules.datapath = RGB2YCbCrDatapath(rgb_w, ycbcr_w, coef_w, coefs)
self.comb += self.datapath.ce.eq(self.pipe_ce)
for name in ["r", "g", "b"]:
self.comb += getattr(self.datapath.sink, name).eq(getattr(sink, name))
for name in ["y", "cb", "cr"]:
self.comb += getattr(source, name).eq(getattr(self.datapath.source, name))
158 changes: 158 additions & 0 deletions hdl/csc/test/common.py
@@ -0,0 +1,158 @@
import random
import copy

from migen.fhdl.std import *
from migen.flow.actor import Sink, Source
from migen.genlib.record import *


def seed_to_data(seed, random=True):
if random:
return (seed * 0x31415979 + 1) & 0xffffffff
else:
return seed


def comp(p1, p2):
r = True
for x, y in zip(p1, p2):
if x != y:
r = False
return r


def check(p1, p2):
p1 = copy.deepcopy(p1)
p2 = copy.deepcopy(p2)
if isinstance(p1, int):
return 0, 1, int(p1 != p2)
else:
if len(p1) >= len(p2):
ref, res = p1, p2
else:
ref, res = p2, p1
shift = 0
while((ref[0] != res[0]) and (len(res) > 1)):
res.pop(0)
shift += 1
length = min(len(ref), len(res))
errors = 0
for i in range(length):
if ref.pop(0) != res.pop(0):
errors += 1
return shift, length, errors


def randn(max_n):
return random.randint(0, max_n-1)


class Packet(list):
def __init__(self, init=[]):
self.ongoing = False
self.done = False
for data in init:
self.append(data)


class PacketStreamer(Module):
def __init__(self, description, last_be=None):
self.source = Source(description)
self.last_be = last_be

# # #

self.packets = []
self.packet = Packet()
self.packet.done = True

def send(self, packet):
packet = copy.deepcopy(packet)
self.packets.append(packet)
return packet

def send_blocking(self, packet):
packet = self.send(packet)
while not packet.done:
yield

def do_simulation(self, selfp):
if len(self.packets) and self.packet.done:
self.packet = self.packets.pop(0)
if not self.packet.ongoing and not self.packet.done:
selfp.source.stb = 1
if self.source.description.packetized:
selfp.source.sop = 1
selfp.source.data = self.packet.pop(0)
self.packet.ongoing = True
elif selfp.source.stb == 1 and selfp.source.ack == 1:
if self.source.description.packetized:
selfp.source.sop = 0
if len(self.packet) == 1:
selfp.source.eop = 1
if self.last_be is not None:
selfp.source.last_be = self.last_be
else:
selfp.source.eop = 0
if self.last_be is not None:
selfp.source.last_be = 0
if len(self.packet) > 0:
selfp.source.stb = 1
selfp.source.data = self.packet.pop(0)
else:
self.packet.done = True
selfp.source.stb = 0


class PacketLogger(Module):
def __init__(self, description):
self.sink = Sink(description)

# # #

self.packet = Packet()

def receive(self):
self.packet.done = False
while not self.packet.done:
yield

def do_simulation(self, selfp):
selfp.sink.ack = 1
if selfp.sink.stb:
if self.sink.description.packetized:
if selfp.sink.sop:
self.packet = Packet()
self.packet.append(selfp.sink.data)
else:
self.packet.append(selfp.sink.data)
if selfp.sink.eop:
self.packet.done = True
else:
self.packet.append(selfp.sink.data)


class AckRandomizer(Module):
def __init__(self, description, level=0):
self.level = level

self.sink = Sink(description)
self.source = Source(description)

self.run = Signal()

self.comb += \
If(self.run,
Record.connect(self.sink, self.source)
).Else(
self.source.stb.eq(0),
self.sink.ack.eq(0),
)

def do_simulation(self, selfp):
n = randn(100)
if n < self.level:
selfp.run = 0
else:
selfp.run = 1

0 comments on commit c099049

Please sign in to comment.