[{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/categories/","section":"Categories","summary":"","title":"Categories"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/categories/computer-science/","section":"Categories","summary":"","title":"Computer Science"},{"content":"Abstractions #Engineers and scientists use abstractions to manage complexity. Before, we relied on working operating systems and software development environment to build useful tools; however, in this course, we are diving deeper into lower level: we are disclosing the digital logic by building gates out of electronic switches - MOFSETs. Then, gates are used to build combinational logic - which all CS students have familarized themselves with after first year through building boolean equations in programming and logical condition in math - and sequential logic - which is introduced through feeding back wires, more on it later!\nFrom Switches to Logic Gates #At the heart of modern electronics are semiconductor devices, which can be engineered to act as tiny electronic switches. These switches, known as MOSFETs (Metal-Oxide-Semiconductor Field-Effect Transistors), are the fundamental building blocks of all digital logic.\nMOSFETs: The Basic Switch #There are two primary types of MOSFETs:\nn-type MOSFET (NMOS): An NMOS transistor has a gate, a source, and a drain. When a high voltage is applied to the gate, it creates a conductive channel between the source and drain, allowing current to flow. It\u0026rsquo;s like a switch that is \u0026ldquo;ON\u0026rdquo; when the control signal is high. p-type MOSFET (PMOS): A PMOS transistor works in the opposite way. When a low voltage is applied to the gate, it creates a conductive channel and allows current to flow. It\u0026rsquo;s a switch that is \u0026ldquo;ON\u0026rdquo; when the control signal is low. Building Logic Gates with CMOS #By combining NMOS and PMOS transistors, we can create Complementary Metal-Oxide-Semiconductor (CMOS) logic gates. This is the dominant technology used in modern integrated circuits.\nInverter (NOT Gate): The simplest logic gate is a NOT gate. It\u0026rsquo;s built with one PMOS and one NMOS transistor. When the input is high, the NMOS turns on, pulling the output low. When the input is low, the PMOS turns on, pulling the output high.\nNAND and NOR Gates: More complex gates like NAND and NOR are also built with specific arrangements of PMOS and NMOS transistors. For example, a NAND gate uses two PMOS transistors in parallel and two NMOS transistors in series.\nAND and OR Gates: While possible to build directly, AND and OR gates are typically created by combining NAND or NOR gates with inverters, as this is more efficient in CMOS design.\nThis ability to construct basic logic gates from simple switches is the first major step up the ladder of abstraction in computer hardware.\nCombinational Logic #Combinational logic circuits are the next level of abstraction. Their outputs are determined solely by their current inputs. These circuits are memoryless.\nRepresenting Logic: Minterms and Maxterms #To design and analyze combinational circuits, we use Boolean algebra. Any Boolean function can be expressed in a canonical form:\nSum of Products (SOP): This form uses minterms. A minterm is a product (AND) term that includes every variable in the function, either in its true or complemented form. The function is then the sum (OR) of the minterms that result in a \u0026lsquo;1\u0026rsquo; output. Product of Sums (POS): This form uses maxterms. A maxterm is a sum (OR) term that includes every variable. The function is the product (AND) of the maxterms that result in a \u0026lsquo;0\u0026rsquo; output. Simplifying Logic: Karnaugh Maps (K-maps) #Boolean expressions can become complex. Karnaugh maps are a graphical method used to simplify them. By arranging the function\u0026rsquo;s truth table in a specific grid, we can visually group adjacent \u0026lsquo;1\u0026rsquo;s (for SOP) or \u0026lsquo;0\u0026rsquo;s (for POS) to find a simpler, equivalent expression. This simplification is crucial for reducing the number of logic gates needed to implement the circuit, making it smaller, faster, and more power-efficient.\nCircuit Delays: Propagation Delay #In the real world, logic gates are not instantaneous. Propagation delay is the time it takes for the output of a gate to change after its inputs have changed. This delay is a critical factor in the performance of digital circuits. The total delay of a combinational circuit is determined by the longest path from any input to any output, known as the critical path.\nCommon Combinational Devices # Adders: Half Adder: A simple circuit that adds two single bits and produces a sum and a carry output. Full Adder: Adds three bits (two input bits and a carry-in bit) and produces a sum and a carry-out. Full adders are the building blocks of larger adders. Ripple-Carry Adder: Multiple full adders can be chained together to add multi-bit numbers. The carry-out of one full adder becomes the carry-in of the next, causing the carry to \u0026ldquo;ripple\u0026rdquo; through the circuit. Sequential Logic #Unlike combinational circuits, the outputs of sequential circuits depend on both the current inputs and the past sequence of inputs. They have \u0026ldquo;memory\u0026rdquo;.\nThe Basic Memory Element: Latches and Flip-Flops # SR Latch: The most fundamental sequential circuit, built by cross-coupling two NOR or NAND gates. It can store a single bit of information. However, it has an undesirable \u0026ldquo;invalid\u0026rdquo; state when both inputs are asserted. SR Flip-Flop: A clocked version of the latch. It only changes state when a clock signal is applied, providing more control. D Flip-Flop (Data/Delay Flip-Flop): A common and useful flip-flop that has a single data input D. The output Q simply takes on the value of D on the active edge of the clock. It\u0026rsquo;s essentially a way to store one bit of data. T Flip-Flop (Toggle Flip-Flop): This flip-flop has a single input T. If T is high, the output Q \u0026ldquo;toggles\u0026rdquo; (flips to its opposite state) on the clock edge. If T is low, the output holds its current state. JK Flip-Flop: A versatile flip-flop that combines the features of the SR and T flip-flops. It can set, reset, hold, or toggle its output. Building with Flip-Flops: Counters #By connecting flip-flops together, we can create counters:\nAsynchronous (Ripple) Counter: The output of one flip-flop serves as the clock input for the next. This design is simple but suffers from a cumulative delay, making it slow for large counters. Synchronous Counter: All flip-flops share a common clock signal and change state simultaneously. This design is faster and more reliable than an asynchronous counter, as it avoids the ripple delay. The Processor #The processor, or Central Processing Unit (CPU), is the brain of the computer, combining combinational and sequential logic to execute instructions.\nMemory # SRAM (Static RAM): Built using latches (similar to flip-flops). It\u0026rsquo;s very fast but also volatile (loses data when power is off) and less dense, meaning it takes up more space per bit. Often used for cache memory. DRAM (Dynamic RAM): Stores each bit as a charge in a capacitor. It\u0026rsquo;s much denser and cheaper than SRAM but requires periodic \u0026ldquo;refreshing\u0026rdquo; to maintain the charge. It\u0026rsquo;s also slower. This is what we typically refer to as \u0026ldquo;main memory\u0026rdquo; or \u0026ldquo;RAM\u0026rdquo;. Tristate Buffer: A special type of buffer that has a third \u0026ldquo;high-impedance\u0026rdquo; state, in which it effectively disconnects its output. Tristate buffers are essential for allowing multiple devices to share a common bus (a set of wires), like a data bus. MIPS Architecture and Datapath # MIPS: A popular RISC (Reduced Instruction Set Computer) architecture. RISC designs emphasize a small, simple, and highly optimized set of instructions. Datapath: The collection of hardware components that perform the actual data processing, including the Arithmetic Logic Unit (ALU), registers, and multiplexers. The datapath is responsible for executing the arithmetic and logical operations specified by the instructions. Control Unit #The control unit is the part of the processor that directs the datapath\u0026rsquo;s operations. It interprets instructions and generates control signals that tell the datapath components what to do. The basic operation of a processor is the fetch-decode-execute cycle:\nFetch: The control unit fetches the next instruction from memory. Decode: The instruction is decoded to determine what operation to perform. Execute: The control unit sends signals to the datapath to execute the operation. Assembly Language #Assembly language is a low-level programming language that provides a symbolic representation of the machine code instructions that a processor can execute.\nMIPS Instructions #MIPS instructions are categorized into three main types:\nR-type (Register): Instructions that operate on data stored in registers. (e.g., add, sub, and) I-type (Immediate): Instructions that operate on a register and an immediate value (a constant encoded in the instruction itself). (e.g., addi, lw, sw) J-type (Jump): Instructions that perform unconditional jumps to a new address in the program. These instructions are translated into 32-bit machine code that the processor can understand. The format of the machine code determines which fields correspond to the operation, source and destination registers, and immediate values. These fields are then used by the control unit to generate the appropriate control signals for the datapath.\nFunction Calls and the Stack #To support function calls, MIPS uses a stack, which is a region of memory used for temporary storage. When a function is called, a stack frame is created to store:\nReturn Address: The address of the instruction to return to after the function completes. Arguments: The parameters passed to the function. Saved Registers: Registers that need to be preserved so they can be restored before the function returns. Local Variables: Variables used only within the function. There are specific conventions for which registers are used for arguments ($a0-$a3), return values ($v0-$v1), and which registers the caller ($t0-$t9) or callee ($s0-$s7) is responsible for saving. Following these conventions is crucial for writing correct and modular assembly code.\n","date":"7 December 2025","permalink":"https://peter-hu12138.github.io/notes/csc258/","section":"Notes","summary":"","title":"CSC258"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/tags/hardware/","section":"Tags","summary":"","title":"Hardware"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/","section":"Jingtian Hu's website","summary":"","title":"Jingtian Hu's website"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/notes/","section":"Notes","summary":"","title":"Notes"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/tags/","section":"Tags","summary":"","title":"Tags"},{"content":"Hi, this is Jingtian Hu. I will be blogging here to post my notes and record and reflect on my experience!\nStay tuned for more info.\n","date":"29 November 2025","permalink":"https://peter-hu12138.github.io/posts/hi/","section":"Posts","summary":"","title":"Hi"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/posts/","section":"Posts","summary":"","title":"Posts"},{"content":" I am still on my way to finish the notes for CSC236.\nIntroduction #Computer Science is about problem-solving: understanding, modelling, and solving problems. How do computer scientists ensure what they do is correct? Logic and math are all you need, and of all the math humans have explored, discrete mathematics is the most relevant to the core of computer science - logic, language, and formal representation of structures. Thus, naturally, a lot of contents in the course concern topics in discrete mathematics if not all.\nInduction #Empirical induction is problematic, at least not rational if not completely unrealiable, but mathematical induction is not as it is based on deductive logic and gurantees truth through propagation along the chain of implication.\nSimple Induction (a.k.a. Weak Induction) #Let \\(P\\) be any predicate on naturals. Principle of Simple Induction is\n$$[P(0) \\wedge \\forall n \\in \\mathbb{N}. P(n) \\implies P(n+1)] \\implies \\forall n \\in \\mathbb{N}. P(n)$$Complete Induction (a.k.a Strong Induction) #Let \\(P\\) be any predicate on naturals. Principle of Complete Induction is\n$$[\\forall n \\in \\mathbb{N}. [\\forall k \\in \\mathbb{N}. k \u003c n \\implies P(k)] \\implies P(n)] \\implies \\forall n \\in \\mathbb{N} P(n)$$Note that the base cases are still there but latent.\nStructural Induction #Speaking responsibly, I have to spend more time explaining recursively defined sets before I introduce structural induction; however, let\u0026rsquo;s just understand them as the \u0026ldquo;smallest\u0026rdquo; sets that derive from basis and a set of derivation rules.\nLet \\(B\\) be a set, \\(f_1, f_2, \u0026hellip;, f_k\\) be a set of derivation functions:\ndefine set \\(S\\) as: $$\\forall b \\in B, b \\in S$$ $$\\forall s_1, s_2, ..., s_m \\in S, f_1(s_1, ...) \\in S, f_2(s_1,) \\in S, ..., f_k(s_1, ...) \\in S$$Then the Principle of Structural Induction states for any arbitrary predicate on \\(S\\):\nproving $$\\forall b \\in B, P(b) \\wedge [\\forall s_1, s_2, ..., s_m, \\bigwedge_i^n {P(s_i)} \\implies \\bigwedge_i^k {P(f_i(s_1, ...)})]$$ is sufficient to show $$\\forall s \\in S, P(s)$$Well Ordering Principle (WOP) #Equivalence of Simple Induction, Complete Induction, and WOP #working on it next (but also mention complexty of formal axioms)\nAlgorithm Correctness #Meaning of correctness:\nIf the precondition is met, then the algorithm will terminate free of error and meet post condition.\nRecursive Algorithm Correctness #What follows are conditions to check for recursive algorithm correctness, the principle goes:\nAssume precondition is met, proving the following suffices as proof of the correctness of the algorithm:\nValid calls (Validity): all the calls are valid (satisfying preconditions), non-recursive ones and recursive ones. Valid recursively (Termination): Size is a function of input whose output is a natural, and we need to prove size is strictly decreasing in consecutive recursive calls by showing each function delegates to recursive calls with a value whose size is smaller. Correct Return Value (CRV): Assuming recursive calls are correct - i.e. if we supplement the calls with correct input values, they will terminate and return correct return values free of error, we prove the algorithm returns correct return values. Why is it correct? The principle relies on Well Ordering Principle and induction under the hood.\nWe can justify termination with \u0026ldquo;valid recursively\u0026rdquo;, if a size function has a codomain of naturals and decreases on consecutive recursive calls always, then any call to the algorithm would generate a decreasing sequence of naturals formed by the sizes of recursive calls on the call stack. Since all decreasing sequences of naturals are finite, which is a corollary of Well Ordering Principle (WOP), the sequence of finite calls terminates.\nFor valid calls and correct return values, we based correctness on correctness of recursive calls, but why can we assume recursive calls are correct and take what follows as a sufficient proof for the correctness of the entire algorithm. Isn\u0026rsquo;t that circular reasoning? It isn\u0026rsquo;t because we are not assuming \\(P(n)\\) is correct and proving \\(P(n)\\) is correct, what we do is essentially assuming \\(P(k)\\) is correct and proving \\(P(n)\\) is correct for some \\(k \u0026lt; n\\) with respect to the size function.\nThis resembles and in fact depends on induction, I will first demonstrate one proof with complete induction. To start, let\u0026rsquo;s assume inputs are of the set \\(C\\) and define \\(P\\) be the predicate that \\(\\forall c \\in C, P(c) \\) denotes the algorithm is correct on input \\(c\\). The ultimate goal is to prove \\(\\forall c \\in C, P(c)\\). To use complete induction, we need a size function to measure inputs; by valid recursively, it could be assumed w.l.o.g. there exists \\(size: C \\to \\mathbb{N}\\).\nAfter setting up, we get this guy with aid of size function: \\(\\forall n \\in \\mathbb{N}, \\forall c \\in C, n = size(c) \\implies P(c)\\); note that the truth of this statement implies the truth of \\(\\forall c \\in C, P(c)\\), you can verify by selecting an arbitrary \\(c \\in C\\), and realize \\(size(c) \\in \\mathbb{N}\\). Therefore it suffices to prove the longer one with complete induction:\nLet \\(n \\in \\mathbb{N}\\) and assume \\(\\forall k \\in \\mathbb{N}, k \u0026lt; n \\implies \\forall c \\in C, k = size(c) \\implies P(c)\\) (I.H.) Since we proved any recursive call made has a smaller size (valid recursively), by I.H., we can work with these recursive calls as if they are correct as they descend to smaller sizes by \u0026ldquo;valid recursively\u0026rdquo; and conclude with complete induction that the algorithm is correct on every input from \\(C\\).\nNote: Intuitively, we can understand it as if we are trying to find a metric - the size function with a codomain of naturals - such that the recursive algorithm is always calling (or delegating) itself with smaller inputs with respect to the metric.\nSimilarly, a structural induction approach could make a proof but perhaps cleaner for this proof omits a definition of a size function but requires a construction of a recurrence definition of the input size. It is then needed that we prove \\(P\\) on the base cases and then on the constructor cases, which could naturally match the writeup of the algorithm.\nIterative Algorithm Correctness #Proving that iterative algorithms are correct is similar, relying on inductive thinking, with differences in how repetitions are handled: iterative algorithms don\u0026rsquo;t call themselves on smaller cases but base their correctness off what was left after previous iterations - a.k.a. Loop Invariants.\nUsually, a iterative algorthm correctness has two major goals, normal termination (terminating free of error) and correct return value, which usually correspond to the following trifold procedure:\nValidity: Every operation and function call are correct. This is usually done through proving variables are of certain types and/ or of certain ranges so that all the operands have the correct types and all the function calls are made with correct input value: satisfying precondition. Termination: Similar to Recursive correctness, we define a bounded, strict monotonic integer variant (cp. size) on loop variables so that by consequences of Well Ordering Principle, we know the sequence of variant at each iteration forms is finite. Correct Return Value: This step requires possibly the most inventiveness out of all three. Unlike recursive algorithm where we can rely on post condition of itself, we have to come up with our invariant that ensures the correctness of return value exiting the loop and just before return. Mathematical Recurrence Formula and Runtime analysis #Master\u0026rsquo;s Theorem #Intuitive \u0026ldquo;Proof\u0026rdquo; of Master\u0026rsquo;s Theorem #Finite Automata, Regular Expression, and Regular Language #TODO: subset construction, general transition maps, regex in today\u0026rsquo;s engines isn\u0026rsquo;t regular\nMyhill-Nerode Theorem \u0026amp; Irregular Language #","date":"29 November 2025","permalink":"https://peter-hu12138.github.io/notes/csc236/","section":"Notes","summary":"","title":"CSC236: Introduction to Theory of Computation"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/tags/theory/","section":"Tags","summary":"","title":"Theory"},{"content":"More on it later!\n","date":"3 November 2025","permalink":"https://peter-hu12138.github.io/projects/easy-comm/","section":"Projects","summary":"","title":"Easy communication"},{"content":"","date":null,"permalink":"https://peter-hu12138.github.io/projects/","section":"Projects","summary":"","title":"Projects"}]