The latching arrangement which can be realized using the smallest number of gates (for the latch itself) is the RS latch (commonly realized as two NAND gates, two NOR gates, or a NAND and a NOR). This circuit will switch one way when one input goes active, and switch the other way when the other input goes active. No other latch is simpler, but generating the two separate inputs required by the RS latch may require more outside logic than would be needed with other latching circuits.
Additionally, when building latches out of gates, it's often necessary to make assumptions about propagation delays--in particular, the ratio between the minimum and maximum propagation gate times. Many latching circuits have inherent race conditions which would fail if a signal were to propagate through e.g. one sequence of five gates faster than it propagated through some other sequence of three. In reality, it's possible to bias a few particular gates so as to guarantee that they will be slower than others, but what are you allowed to do here? Conceptually, I like the notion of designing circuits with two clock inputs, which require that the clocks switch in a particular sequence but which will be totally free of internal race conditions provided the clocks are sequenced correctly. Given such a circuit, if one builds a clock sequencer whose time between output clocks exceeds the maximum propagation delay in the downstream circuitry, ensuring that the proper signals will "win" a race condition in the clock sequencer will ensure there are no other "surprise" race conditions in the design.
If one is allowed to regard latching devices as 'primitive components', the most versatile one is the JK flip flop; a T flip flop is essentially a JK flip flop with the two inputs tied together. Counting is often slightly easier with a T flip flop than a D flip flop, but loading and shifting operations are easier with a D flip flop; reasoning about the overall circuit may be easier with a D flip flop than the other types, so that's what I'd suggest starting with.
Conceptually, I would suggest figuring out what each bit is supposed to do in relation to the various states. There are two approaches I'd suggest trying:
- Regard a "shift" as being similar to a "count", except that (1) each bit's "carry out" will reflect the state of the bit, without rather than being the bit value "anded" with the "carry in", and (2) each bit's "new value" will simply be the carry in, rather than being the carry in xor'ed with the old value.
- Use a mux to distinguish "count" modes from "load" modes, and then use another mux to distinguish "load with old value shifted one bit" from "load with value on parallel input".
I would expect the first approach would probably yield slightly less circuitry. Note that in either case, "circular rotate" sounds the same as "serial-load", except that a mux feeds the most significant bit back to the shift/carry input.