codexpast-exam-of-the-year-1-of-the-computer-science-course-of-the-university-of-oxford2003computing-prelims-2003-pulse.py
past-exam-of-the-year-1-of-the-computer-science-course-of-the-university-of-oxford/2003/computing-prelims-2003-pulse.py
"""Draw pulse generator state chart and the original sampled timing sequence.
Writes PNG in cwd. Python 3.14.4, matplotlib 3.10.7, numpy 2.3.5.
"""
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, FancyArrowPatch
import numpy as np
plt.rcParams.update({'font.size':11,'font.family':'DejaVu Sans'})
fig=plt.figure(figsize=(9,6.8),facecolor='white');gs=fig.add_gridspec(2,1,height_ratios=[1.2,1],hspace=.4)
ax=fig.add_subplot(gs[0]);ax.set(xlim=(-3,3),ylim=(-1.15,1.65),aspect='equal');ax.axis('off')
pos={'Ready':(-2,0),'Pulse':(0,.7),'Wait':(2,0)}
for s,p in pos.items():
ax.add_patch(Circle(p,.43,facecolor='#ffe0af' if s=='Pulse' else '#e6f1fa',edgecolor='#264f70',lw=1.5))
ax.text(*p,s+'\nz='+('1' if s=='Pulse' else '0'),ha='center',va='center')
edges=[('Ready','Pulse','10',-.1,(-1,1)),('Pulse','Ready','01,11',-.1,(-1,.03)),('Pulse','Wait','00,10',.05,(1,.8)),('Wait','Ready','01,11',-.35,(0,-.85))]
for s,t,lab,rad,loc in edges:
ax.add_patch(FancyArrowPatch(pos[s],pos[t],shrinkA=32,shrinkB=32,connectionstyle=f'arc3,rad={rad}',arrowstyle='-|>',mutation_scale=14,color='#264f70',lw=1.4))
ax.text(*loc,lab,ha='center',bbox={'facecolor':'white','edgecolor':'none','pad':1})
for s,lab in [('Ready','00,01,11'),('Wait','00,10')]:
x,y=pos[s];sgn=-1 if s=='Ready' else 1
ax.add_patch(FancyArrowPatch((x+sgn*.3,y+.3),(x+sgn*.3,y-.3),connectionstyle=f'arc3,rad={-2*sgn}',arrowstyle='-|>',mutation_scale=13,color='#264f70'))
ax.text(x+sgn*.68,y,lab,ha='center',rotation=90,fontsize=9)
ax.annotate('start',xy=(-2.33,.3),xytext=(-2.65,1),arrowprops={'arrowstyle':'->'},ha='center')
ax.set_title('One-cycle pulse, with reset priority for ab = 11',weight='bold')
a=fig.add_subplot(gs[1]);time=np.arange(17)
as_=[0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,0]
bs_=[0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0]
s='Ready';zs=[]
for av,bv in zip(as_,bs_):
s='Ready' if bv else ('Pulse' if s=='Ready' and av else 'Ready' if s=='Ready' else 'Wait')
zs.append(int(s=='Pulse'))
for vals,offset,col in [(as_,4,'#39688a'),(bs_,2,'#39688a'),(zs,0,'#ae6325')]:
# Sampled inputs are shown through each edge; z is the state after that edge.
a.step(time,np.array(vals+[vals[-1]])*.7+offset,where='post',color=col,lw=1.7)
for k in range(16):a.axvline(k,color='.85',lw=.6,zorder=-1)
a.set(xlim=(0,16),ylim=(-.2,5.1),yticks=[.3,2.3,4.3],yticklabels=['z after edge','b sampled','a sampled'],xticks=np.arange(16)+.05,xticklabels=np.arange(1,17),xlabel='Clock edge number (values held until the next edge)')
a.tick_params(axis='x',labelsize=9);a.spines[['top','right']].set_visible(False)
a.set_title('Source timing: pulses after edges 2, 10, 13 and 15',fontsize=11)
fig.savefig(Path.cwd()/'computing-prelims-2003-pulse.png',dpi=120,facecolor='white',bbox_inches='tight',metadata={'Software':'computing-prelims-2003-pulse.py'})
plt.close(fig)